diff --git a/example/out/ZDWorld-2.13.6-zdw1-debug-asset.pk3 b/example/out/ZDWorld-2.13.6-zdw1-debug-asset.pk3 index e851a17..684e7f7 100644 Binary files a/example/out/ZDWorld-2.13.6-zdw1-debug-asset.pk3 and b/example/out/ZDWorld-2.13.6-zdw1-debug-asset.pk3 differ diff --git a/example/out/ZDWorld-2.13.6-zdw1-debug-code.pk3 b/example/out/ZDWorld-2.13.6-zdw1-debug-code.pk3 index 0009cdf..efeb433 100644 Binary files a/example/out/ZDWorld-2.13.6-zdw1-debug-code.pk3 and b/example/out/ZDWorld-2.13.6-zdw1-debug-code.pk3 differ diff --git a/example/out/ZDWorld-2.13.6-zdw1-release-asset.pk3 b/example/out/ZDWorld-2.13.6-zdw1-release-asset.pk3 index bd6b175..274d076 100644 Binary files a/example/out/ZDWorld-2.13.6-zdw1-release-asset.pk3 and b/example/out/ZDWorld-2.13.6-zdw1-release-asset.pk3 differ diff --git a/example/out/ZDWorld-2.13.6-zdw1-release-code.pk3 b/example/out/ZDWorld-2.13.6-zdw1-release-code.pk3 index d49f9be..b0ca359 100644 Binary files a/example/out/ZDWorld-2.13.6-zdw1-release-code.pk3 and b/example/out/ZDWorld-2.13.6-zdw1-release-code.pk3 differ diff --git a/example/out/ZDWorld-2.13.6-zdw1-release-foes-asset.pk3 b/example/out/ZDWorld-2.13.6-zdw1-release-foes-asset.pk3 index 399cbef..3517db4 100644 Binary files a/example/out/ZDWorld-2.13.6-zdw1-release-foes-asset.pk3 and b/example/out/ZDWorld-2.13.6-zdw1-release-foes-asset.pk3 differ diff --git a/example/out/ZDWorld-2.13.6-zdw1-release-foes-code.pk3 b/example/out/ZDWorld-2.13.6-zdw1-release-foes-code.pk3 index 41006d6..f3f5c3e 100644 Binary files a/example/out/ZDWorld-2.13.6-zdw1-release-foes-code.pk3 and b/example/out/ZDWorld-2.13.6-zdw1-release-foes-code.pk3 differ diff --git a/zdcode/bundle.py b/zdcode/bundle.py index 7e74af2..b822bd2 100644 --- a/zdcode/bundle.py +++ b/zdcode/bundle.py @@ -149,23 +149,19 @@ def store_collected(self, out_zip: zipfile.ZipFile, target: str, data: bytes): def assemble(self): """Internally assemble a zip file to then store at an output path.""" - zips: dict[str, zipfile.ZipFile] = {} - zipfiles = set() - - for oname, out in self.bundle.outputs.items(): - oname = oname.lower() - - zipf = zipfile.ZipFile(out.output, mode="w") - zips[oname] = zipf - zipfiles.add(zipf) + routing = {} while self.collected: target, oname, data = self.collected.popleft() - out_zip = zips[oname.lower()] - self.store_collected(out_zip, target, data) + oname = oname.lower() + routing.setdefault(oname, []).append((target, data)) + + for oname, out in self.bundle.outputs.items(): + oname = oname.lower() - for zipf in zips.values(): - zipf.close() + with zipfile.ZipFile(out.output, mode="w") as zipf: + for item in routing[oname]: + self.store_collected(zipf, *item) def scan_dep(self, url: pathlib.Path, target: pathlib.PurePath): """Scan for a single dependency.""" diff --git a/zdcode/compiler/compiler.py b/zdcode/compiler/compiler.py index db74022..1627ebc 100644 --- a/zdcode/compiler/compiler.py +++ b/zdcode/compiler/compiler.py @@ -145,7 +145,7 @@ def _parse_parameter(self, parm, context, name=None): return self._parse_anonym_macro(*pval, context, name) def _parse_anonym_macro(self, args, body, context, name=None): - name = name or f"ANONYMMACRO_{self.id.upper()}_{self.num_anonym_macros}" + name = name or f"ANONYMMACRO_{self.identifier.upper()}_{self.num_anonym_macros}" self.num_anonym_macros += 1 context.macros[name.upper()] = (args, body) @@ -167,11 +167,13 @@ def _parse_literal_numeric(self, literal, context): return float(context.replacements[literal[1].upper()]) raise CompilerError( - f"Cannot get compile-time variable for evaluation, {repr(literal[1])}, in {context.describe()}" + "Cannot get compile-time variable for evaluation, " + f"{repr(literal[1])}, in {context.describe()}" ) raise CompilerError( - f"Could not parse numeric expression {repr(literal)} at {context.describe()}" + "Could not parse numeric expression " + f"{repr(literal)} at {context.describe()}" ) def _parse_literal(self, literal, context): @@ -239,7 +241,9 @@ def _parse_template_derivation( if len(template_parms) != len(template.template_parameters): raise CompilerError( - f"Bad number of template parameters for '{template_name}' in {context.describe()}: expected {len(template.template_parameters)}, got {len(template_parms)}" + f"Bad number of template parameters for '{template_name}' " + f"in {context.describe()}: expected " + f"{len(template.template_parameters)}, got {len(template_parms)}" ) template_parms = [ @@ -360,7 +364,8 @@ def _parse_formatted_string(self, cval, context: ZDCodeParseContext): else: raise CompilerError( - f"Replacement {pval} not found while formatting string in {context.describe()}" + f"Replacement {pval} not found " + f"while formatting string in {context.describe()}" ) return "".join(unstringify(x) for x in res) @@ -385,7 +390,8 @@ def _parse_replaceable_number(self, cval, context: ZDCodeParseContext): except ValueError: raise CompilerError( - f"Invalid repeat count in {context.describe()}: expected valid integer, got {repr(cval)}" + f"Invalid repeat count in {context.describe()}: " + f"expected valid integer, got {repr(cval)}" ) else: @@ -483,7 +489,8 @@ def recurse(modifier_chars): except KeyError: raise CompilerError( - f"No parameter {cval} for replacement within modifier, in {context.describe()}!" + f"No parameter {cval} for replacement within modifier, " + f"in {context.describe()}!" ) elif ctype == "recurse": @@ -526,14 +533,16 @@ def _parse_state_sprite(self, context: ZDCodeParseContext, sprite): else: raise CompilerError( - f"Parametrized sprite '{sprite_name}' in {context.describe()} needs to be passed a string; got {repr(new_name)}" + f"Parametrized sprite '{sprite_name}' in {context.describe()} " + f"needs to be passed a string; got {repr(new_name)}" ) name = new_name except KeyError: raise CompilerError( - f"No parameter {repr(sprite_name)} for parametrized sprite name, in {context.describe()}!" + f"No parameter {repr(sprite_name)} for parametrized sprite name, " + f"in {context.describe()}!" ) return name @@ -628,7 +637,8 @@ def clear_remotes(target=context): elif s[0] == "call": raise CompilerError( - f"Functions and calls have been removed since ZDCode 2.11.0! ({context.describe()})" + "Functions and calls have been removed since ZDCode 2.11.0! " + f"({context.describe()})" ) elif s[0] == "flow": @@ -682,7 +692,8 @@ def clear_remotes(target=context): except KeyError: raise CompilerError( - f"Tried to apply unkown state mod {repr(apply_mod)} in apply statement inside {context.describe()}!" + f"Tried to apply unkown state mod {repr(apply_mod)} " + f"in apply statement inside {context.describe()}!" ) apply_ctx = context.derive("apply block") @@ -962,7 +973,7 @@ def _parse_anonym_class(self, anonym_class, context) -> str: a = dict(anonym_class) new_context = context.derive("anonymous class") - classname = f"_AnonymClass_{self.id}_{len(self.anonymous_classes)}" + classname = f"_AnonymClass_{self.identifier}_{len(self.anonymous_classes)}" if a["group"]: g = unstringify(a["group"]) @@ -994,7 +1005,7 @@ def _parse_anonym_class(self, anonym_class, context) -> str: def _derive_class_from_template( self, - template, + template: ZDClassTemplate, param_values, context, labels=(), @@ -1012,7 +1023,6 @@ def _derive_class_from_template( body = list(body) needs_init, actor = template.generate_init_class( - self, context, param_values, {l.upper() for l in labels.keys()}, @@ -1205,6 +1215,15 @@ def do_else(): else: yield from do_else() + def assert_group_exists( + self, groupname: str, ctx_str: str, context: "ZDCodeParseContext" + ): + """Raises an error if this group does not exist.""" + if groupname not in self.groups: + raise CompilerError( + f"No such group '{groupname}' {ctx_str} (in f{context.describe()})!" + ) + def _parse(self, actors, debug=False): parsed_actors = [] @@ -1391,13 +1410,11 @@ def pending_oper(): self.actors.extend(parsed_actors) self.actors.sort(key=lambda actor: actor.name) # predominantly alphabetic sort - reorders = self.reorder_inherits() + self.reorder_inherits() if debug: context.print_state_tree() - # print("(Reordered {} actors)".format(reorders)) - def __init__(self): self.includes = {} self.inventories = [] @@ -1405,7 +1422,7 @@ def __init__(self): self.actors = [] self.actor_names = {} self.groups = {} - self.id = make_id(35) + self.identifier = make_id(35) self.num_anonym_macros = 0 def reorder_inherits(self) -> int: @@ -1433,7 +1450,7 @@ def reorder_inherits(self) -> int: def to_decorate(self): res = TextNode(indent=0) - res.add_line(f"// :ZDCODE version='{__VERSION__}' id='{self.id}' ") + res.add_line(f"// :ZDCODE version='{__VERSION__}' id='{self.identifier}' ") if self.inventories: for i in self.inventories: diff --git a/zdcode/compiler/context.py b/zdcode/compiler/context.py index 4d1f79a..6d9256e 100644 --- a/zdcode/compiler/context.py +++ b/zdcode/compiler/context.py @@ -1,3 +1,4 @@ +"""ZDCode processing context.""" import collections import itertools from typing import TYPE_CHECKING @@ -11,6 +12,8 @@ class ZDCtxDescBlock: + """ZDCode context description block.""" + def __init__(self, ctx: "ZDCodeParseContext", desc: str): self.ctx = ctx self.desc = desc @@ -22,7 +25,12 @@ def __exit__(self, _1, _2, _3): assert self.ctx.desc_stack.pop() == self.desc -class ZDCodeParseContext(object): +class ZDCodeParseContext: + """A ZDCode processing and transform context. + + Used to group states and other objects, to count relevant gruopings of states, + and to help with debugging.""" + def __init__( self, replacements=None, @@ -65,16 +73,18 @@ def __init__( self.add_description(description) def add_description(self, desc): + """Appends to the description stack of this context.""" self.desc_stack.append(desc) def get_applied_mods(self): + """Returns an iterator on the state modifiers active in this context.""" if self.always_applied_mods is None: return iter(self.applied_mods) - else: - return itertools.chain(self.always_applied_mods, self.applied_mods) + return itertools.chain(self.always_applied_mods, self.applied_mods) def print_state_tree(self, _print_low=print, _print=print, prefix="+ "): + """Print a tree of everything in this context.""" ended = 0 _print_low = _print_low or print @@ -108,16 +118,16 @@ def _print_next(line=""): imax = len(self.states) - for i, s in enumerate(self.states): - ended = 1 if i >= imax - 1 else 0 + for index, state in enumerate(self.states): + ended = 1 if index >= imax - 1 else 0 - if isinstance(s, ZDCodeParseContext): - s.print_state_tree( + if isinstance(state, ZDCodeParseContext): + state.print_state_tree( _print, _print_next, "'---+ " if ended > 0 else "+---+ " ) else: - _branch(f"{type(s).__name__} ({s.num_states()})", ended) + _branch(f"{type(state).__name__} ({state.num_states()})", ended) if not ended: _print("|") @@ -128,10 +138,10 @@ def _print_next(line=""): imax = len(self.remote_children) - for i, ch in enumerate(self.remote_children): - ended = 2 if i >= imax - 1 else 1 + for index, child in enumerate(self.remote_children): + ended = 2 if index >= imax - 1 else 1 - ch.print_state_tree( + child.print_state_tree( _print, _print_next, "^---* (remote) " if ended > 1 else "%---* (remote) ", @@ -141,16 +151,18 @@ def _print_next(line=""): _print(":") def num_states(self): + """Counts every state in this context and children, except remotely derived.""" return sum(s.num_states() for s in self.states) def remote_num_states(self): + """Counts every state in this context and its children.""" return ( self.remote_offset + sum( - s.remote_num_states() - if isinstance(s, ZDCodeParseContext) - else s.num_states() - for s in self.states + state.remote_num_states() + if isinstance(state, ZDCodeParseContext) + else state.num_states() + for state in self.states ) + sum(c.remote_num_states() for c in self.remote_children) ) @@ -162,6 +174,13 @@ def remote_derive( break_ctx: "ZDCodeParseContext" | Literal["self"] | None = None, loop_ctx: "ZDCodeParseContext" | Literal["self"] | None = None, ) -> "ZDCodeParseContext": + """Creates a new context, which 'remotely' derives from this one. + + Remote derivation is special in that a remotely derived context does not + actually count as a child of the current one. Its states are not counted + towards this one's total, unless using [remote_num_states]. + """ + # derives without adding to states res = ZDCodeParseContext( self.replacements, @@ -189,6 +208,7 @@ def derive( break_ctx: "ZDCodeParseContext" | Literal["self"] | None = None, loop_ctx: "ZDCodeParseContext" | Literal["self"] | None = None, ) -> "ZDCodeParseContext": + """Creates a child context deriving from this one, and returns it.""" res = ZDCodeParseContext( self.replacements, self.macros, @@ -212,25 +232,31 @@ def __repr__(self): return f"ZDCodeParseContext({self.repr_describe()})" def desc_block(self, desc: str): + """Returns a context description block to use with 'with'.""" return ZDCtxDescBlock(self, desc) def update(self, other_ctx: "ZDCodeParseContext"): + """Update ChainMaps to also point to the other context's information.""" self.macros.maps.insert(-1, other_ctx.macros) self.replacements.maps.insert(-1, other_ctx.replacements) self.templates.maps.insert(-1, other_ctx.templates) self.mods.maps.insert(-1, other_ctx.mods) def add_actor(self, ac: "ZDActor"): - for al in self.actor_lists: - al.append(ac) + """Append an actor to every actor list tracked by this context.""" + for actor_list in self.actor_lists: + actor_list.append(ac) - def describe(self): + def describe(self) -> str: + """Describes this context.""" return " at ".join(self.desc_stack[::-1]) - def repr_describe(self): + def repr_describe(self) -> str: + """Describes this context, but comma separated. Used in __repr__.""" return ", ".join(self.desc_stack[::-1]) def resolve(self, name, desc="a parametrizable name"): + """Resolve a name, applying replacements in this context when applicable.""" while name[0] == "@": resolves = len(name) - len(name.lstrip("@")) casename = name[resolves:] @@ -245,7 +271,8 @@ def resolve(self, name, desc="a parametrizable name"): else: raise CompilerError( - f"No such replacement {repr(name)} while trying to resolve {repr(casename)} in {self.describe()}!" + f"No such replacement {repr(name)} " + f"while trying to resolve {repr(casename)} in {self.describe()}!" ) return name diff --git a/zdcode/compiler/error.py b/zdcode/compiler/error.py index 9fe65e1..5ef2b1f 100644 --- a/zdcode/compiler/error.py +++ b/zdcode/compiler/error.py @@ -1,2 +1,5 @@ +"""ZDCode compiler errors.""" + + class CompilerError(Exception): - pass + """An error encountered during compilation.""" diff --git a/zdcode/compiler/parser.py b/zdcode/compiler/parser.py index 73802a8..74c92d9 100644 --- a/zdcode/compiler/parser.py +++ b/zdcode/compiler/parser.py @@ -1,4 +1,5 @@ # pylint: disable=unreachable +"""The parser. Uses parsy as the backend.""" import glob import json import math @@ -16,14 +17,14 @@ from parsy import success from parsy import whitespace -s = string +st = string fa = fail whitespace = whitespace.desc("whitespace") wo = whitespace.optional() -def istring(st): - return s(st, transform=lambda s: s.upper()) +def istring(stringen): + return st(stringen, transform=lambda s: s.upper()) ist = istring @@ -38,14 +39,14 @@ def istring(st): | regex(r"u[0-9a-fA-F]{4}").map(lambda s: chr(int(s[1:], 16))) | regex(r"x[0-9a-fA-F]{2}").map(lambda s: chr(int(s[1:], 16))) ) -string_delim_1 = s('"') -string_delim_2 = s("'") +string_delim_1 = st('"') +string_delim_2 = st("'") string_body_plex_1 = (string_body_1 | string_esc).many().concat() string_body_plex_2 = (string_body_2 | string_esc).many().concat() string_literal = (string_delim_1 >> string_body_plex_1 << string_delim_1) | ( string_delim_2 >> string_body_plex_2 << string_delim_2 ) -lwhitespace = whitespace | s("\n") | s("\r") +lwhitespace = whitespace | st("\n") | st("\r") ifneq = re.compile(r"^\#IF(N|NOT)EQ(UALS?)?$") ifundef = re.compile(r"^\#IF(U?N|NOT)DEF(INED)?$") @@ -96,11 +97,11 @@ def __str__(self): def state_modifier_name(): return ( ( - s("{") + st("{") >> regex(r"[a-zA-Z_][a-zA-Z_0-9]*") .desc("modifier parameter name") .tag("replace") - << s("}") + << st("}") | seq( ist("(").tag("part"), state_modifier_name.tag("recurse"), @@ -117,7 +118,7 @@ def state_modifier_name(): @generate def modifier(): # State modifier. Not to be confused with the 'mod' modifier block. - return string("[").then(state_modifier_name).skip(s("]")) + return string("[").then(state_modifier_name).skip(st("]")) yield @@ -164,7 +165,7 @@ def p_group_name(): def p_range_vals(): return seq( (replaceable_number | success(0)) << wo, - ist("..") >> wo >> (s("=") >> wo >> replaceable_number).tag(1) + ist("..") >> wo >> (st("=") >> wo >> replaceable_number).tag(1) | replaceable_number.tag(0), ) yield @@ -181,7 +182,7 @@ def format_string_literal(): return ( ist("f") >> wo - >> s("{") + >> st("{") >> wo >> ( ( @@ -191,7 +192,7 @@ def format_string_literal(): ).sep_by(wo) ) << wo - << s("}") + << st("}") ) yield @@ -326,7 +327,7 @@ def eval_operation(): @generate def numeric_eval(): - return ist("e") >> wo >> s("{") >> eval_body << s("}") + return ist("e") >> wo >> st("{") >> eval_body << st("}") yield @@ -345,7 +346,7 @@ def literal(): @generate def array_literal(): - return (wo >> s("{") >> wo >> expression.sep_by(s(",") << wo) << s("}")).tag( + return (wo >> st("{") >> wo >> expression.sep_by(st(",") << wo) << st("}")).tag( "array" ) yield @@ -353,7 +354,7 @@ def array_literal(): @generate def paren_expr(): - return s("(") >> expression << s(")") + return st("(") >> expression << st(")") yield @@ -415,7 +416,7 @@ def template_parameter_list(): @generate def expr_argument_list(): - return arg_expression.sep_by(s(",") >> wo) + return arg_expression.sep_by(st(",") >> wo) yield @@ -433,7 +434,7 @@ def parameter(): def specialized_parameter_list(ptype): @generate def plist(): - return ptype.sep_by(s(",") >> wo, min=1) + return ptype.sep_by(st(",") >> wo, min=1) yield return plist @@ -456,7 +457,7 @@ def actor_function_call(): def state_call(): return seq( regex(r"[a-zA-Z_][a-zA-Z_0-9]*").desc("called state function name").skip(wo), - s("(").then(wo).then(expr_argument_list).skip(wo).skip(s(")")).optional(), + st("(").then(wo).then(expr_argument_list).skip(wo).skip(st(")")).optional(), ) yield @@ -483,7 +484,7 @@ def break_statement(): def call_literal(): return seq( regex(r"[a-zA-Z_][a-zA-Z_0-9]*").desc("called expression function name") << wo, - (s("(") >> wo >> expr_argument_list << wo << s(")")), + (st("(") >> wo >> expr_argument_list << wo << st(")")), ) yield @@ -517,8 +518,8 @@ def templated_class_derivation(): return seq( regex(r"[a-zA-Z_][a-zA-Z_0-9]*").desc("name of templated class").skip(wo), ( - (s("::()") >> success([])) - | (s("::(").then(wo).then(parameter_list).skip(wo).skip(s(")"))) + (st("::()") >> success([])) + | (st("::(").then(wo).then(parameter_list).skip(wo).skip(st(")"))) ), ( (whitespace >> (ist("inherits") | ist("extends") | ist("expands"))) @@ -533,7 +534,7 @@ def templated_class_derivation(): .map(lambda x: x or None) .tag("group"), ( - wo.then(s("{")) + wo.then(st("{")) .then( wo.then( ( @@ -551,7 +552,7 @@ def templated_class_derivation(): regex(r"[a-zA-Z\_][a-zA-Z\_0-9]*") .desc("macro name") .tag("name"), - (wo >> s("(") >> macro_argument_list << s(")") << wo) + (wo >> st("(") >> macro_argument_list << st(")") << wo) .optional() .map(lambda a: a or []) .tag("args"), @@ -565,9 +566,9 @@ def templated_class_derivation(): >> regex(r"[a-zA-Z0-9\_\.]+").tag("name"), ( (whitespace >> ist("to") << whitespace) - | (wo >> s("=") << wo) + | (wo >> st("=") << wo) ).desc("'to' or equal sign") - >> parameter.sep_by((s(",") << wo)).tag("value"), + >> parameter.sep_by((st(",") << wo)).tag("value"), ) .map(dict) .tag("property") @@ -576,12 +577,12 @@ def templated_class_derivation(): (ist("var") << whitespace) >> seq( regex(r"user_[a-zA-Z0-9_]+").desc("var name").tag("name"), - (wo.then(s("[")).then(replaceable_number).skip(s("]"))) + (wo.then(st("[")).then(replaceable_number).skip(st("]"))) .optional() .map(lambda x: int(x or 0)) .tag("size"), ( - wo.then(s(":")) + wo.then(st(":")) .then(wo) .then(regex(r"[a-zA-Z_.][a-zA-Z0-9_]+")) ) @@ -590,7 +591,7 @@ def templated_class_derivation(): .map(lambda t: t or "int") .tag("type"), ( - wo.then(s("=")).then( + wo.then(st("=")).then( expression.tag("val") | array_literal.tag("arr") ) ) @@ -604,7 +605,7 @@ def templated_class_derivation(): (ist("array") << whitespace) >> seq( regex(r"user_[a-zA-Z0-9_]+").desc("array name").tag("name"), - (wo >> s("=") >> wo >> array_literal) + (wo >> st("=") >> wo >> array_literal) .desc("array values") .tag("value"), ) @@ -615,12 +616,12 @@ def templated_class_derivation(): | mod_block.tag("mod") ) .skip(wo) - .skip(s(";").optional().optional()) + .skip(st(";").optional().optional()) .skip(wo) .many() .optional() ) - .skip(s("}")) + .skip(st("}")) ) .optional() .map(lambda x: x or []), @@ -674,7 +675,7 @@ def static_template_derivation(): ist("as") >> whitespace >> templated_class_derivation.tag("source"), ) ) - << s(";").optional() + << st(";").optional() << wo ) yield @@ -699,7 +700,7 @@ def mod_block(): @generate def nested_class_body(): - return s("{") >> class_body.many().optional() << s("}") + return st("{") >> class_body.many().optional() << st("}") yield @@ -711,7 +712,7 @@ def class_body(): ist("macro") >> whitespace >> regex(r"[a-zA-Z_][a-zA-Z_0-9]*").desc("macro name").tag("name"), - (wo >> s("(") >> macro_argument_list << s(")") << wo) + (wo >> st("(") >> macro_argument_list << st(")") << wo) .optional() .map(lambda a: a or []) .tag("args"), @@ -722,10 +723,10 @@ def class_body(): | seq( (ist("set") >> whitespace).desc("'set' keyword") >> regex(r"[a-zA-Z0-9_\.]+").tag("name"), - ((whitespace >> ist("to") << whitespace) | (wo >> s("=") << wo)).desc( + ((whitespace >> ist("to") << whitespace) | (wo >> st("=") << wo)).desc( "'to' or equal sign" ) - >> parameter.sep_by((s(",") << wo)).tag("value"), + >> parameter.sep_by((st(",") << wo)).tag("value"), ) .map(dict) .tag("property") @@ -736,17 +737,17 @@ def class_body(): | (ist("var") << whitespace) >> seq( regex(r"user_[a-zA-Z0-9_]+").desc("var name").tag("name"), - (wo >> s("[") >> replaceable_number << s("]")) + (wo >> st("[") >> replaceable_number << st("]")) .desc("array size") .optional() .map(lambda x: int(x or 0)) .tag("size"), - (wo >> s(":") >> wo >> regex(r"[a-zA-Z_.][a-zA-Z0-9_]+")) + (wo >> st(":") >> wo >> regex(r"[a-zA-Z_.][a-zA-Z0-9_]+")) .desc("var type") .optional() .map(lambda t: t or "int") .tag("type"), - (wo >> s("=") >> (expression.tag("val") | array_literal.tag("arr"))) + (wo >> st("=") >> (expression.tag("val") | array_literal.tag("arr"))) .optional() .tag("value"), ) @@ -771,7 +772,7 @@ def class_body(): | global_apply.tag("apply") | class_for_loop.tag("for") ).skip(wo) - << s(";").optional() + << st(";").optional() << wo ) yield @@ -783,7 +784,7 @@ def abstract_label_body(): (ist("abstract label") | ist("abstract state")) >> whitespace >> regex(r"[a-zA-Z_]+").desc("label name") - << s(";").optional() + << st(";").optional() ) yield @@ -795,18 +796,18 @@ def abstract_array_body(): ist("abstract array") >> whitespace >> regex(r"user_[a-zA-Z0-9_]+").desc("array name").tag("name"), - (wo >> s("[") >> replaceable_number << s("]")) + (wo >> st("[") >> replaceable_number << st("]")) .optional() .map(lambda x: int(x) if x else "any") .tag("size"), - (wo >> s(":") >> wo >> regex(r"[a-zA-Z_.][a-zA-Z0-9_]+")) + (wo >> st(":") >> wo >> regex(r"[a-zA-Z_.][a-zA-Z0-9_]+")) .desc("var type") .optional() .map(lambda t: t or "int") .tag("type"), ).map(dict) << wo - << s(";").optional() + << st(";").optional() << wo ) yield @@ -820,13 +821,13 @@ def abstract_macro_body(): >> whitespace >> regex(r"[a-zA-Z_]+").desc("macro name").tag("name") << wo, - (s("(") >> wo >> macro_argument_list << wo << s(")")) + (st("(") >> wo >> macro_argument_list << wo << st(")")) .optional() .map(lambda x: x or []) .tag("args"), ).map(dict) << wo - << s(";").optional() + << st(";").optional() << wo ) yield @@ -834,7 +835,7 @@ def abstract_macro_body(): @generate def sprite_name(): - return (s("####") | s('"####"') | regex(r"[A-Z0-9_]{4}")).tag("normal") | ( + return (st("####") | st('"####"') | regex(r"[A-Z0-9_]{4}")).tag("normal") | ( ist("param") >> whitespace >> regex(r"[a-zA-Z_][a-zA-Z_0-9]*") ).tag("parametrized") yield @@ -853,12 +854,12 @@ def group_declaration(): ( (ist("group") << whitespace).desc("group statement") >> wo >> group_name ).tag("name"), - (wo >> s("{") >> regex(r"[a-zA-Z0-9_]+").sep_by(s(",") << wo) << s("}")) + (wo >> st("{") >> regex(r"[a-zA-Z0-9_]+").sep_by(st(",") << wo) << st("}")) .optional() .map(lambda x: x if x and tuple(x) != ("",) else []) .tag("items"), ) - << s(";").optional() + << st(";").optional() ) yield @@ -885,7 +886,7 @@ def actor_class(): .optional() .tag("replacement") .desc("replacement"), - (whitespace >> s("#") >> regex(r"[0-9]+")) + (whitespace >> st("#") >> regex(r"[0-9]+")) .desc("class number") .map(int) .optional() @@ -893,8 +894,8 @@ def actor_class(): .desc("class number") .skip(wo), ( - (s("{") >> wo >> class_body.many().optional() << wo.then(s("}")).skip(wo)) - | s(";").optional().map(lambda _: []) + (st("{") >> wo >> class_body.many().optional() << wo.then(st("}")).skip(wo)) + | st(";").optional().map(lambda _: []) ).tag("body"), ) yield @@ -904,9 +905,9 @@ def actor_class(): def templated_actor_class(): return seq( (ist("actor") | ist("class") | ist("template")).desc("class template") - >> s("<") + >> st("<") >> template_parameter_list.tag("parameters") - << s(">") + << st(">") << wo, formattable_classname.desc("class name").tag("classname"), ((whitespace >> ist("group") << whitespace).desc("group keyword") >> group_name) @@ -926,7 +927,7 @@ def templated_actor_class(): .optional() .tag("replacement") .desc("replacement"), - (whitespace >> s("#") >> regex(r"[0-9]+")) + (whitespace >> st("#") >> regex(r"[0-9]+")) .desc("class number") .map(int) .optional() @@ -934,7 +935,7 @@ def templated_actor_class(): .desc("class number") .skip(wo), ( - s("{") + st("{") >> wo >> ( abstract_macro_body.desc("abstract macro").tag("abstract macro") @@ -944,7 +945,7 @@ def templated_actor_class(): ) .many() .optional() - << wo.then(s("}")).skip(wo) + << wo.then(st("}")).skip(wo) ).tag("body"), ) yield @@ -983,7 +984,7 @@ def normal_state(): return ( seq( sprite_name.desc("state name").skip(wo), - (regex(r"[A-Z_.]").many() | s('"#"') | s("#")) + (regex(r"[A-Z_.]").many() | st('"#"') | st("#")) .desc("state sprite") .skip(wo), regex(r"\-?\d+") @@ -1032,10 +1033,10 @@ def state_action(): @generate def action_body(): return ( - s("{") + st("{") >> wo - >> (state_action << s(";").optional() << wo).many().optional() - << s("}") + >> (state_action << st(";").optional() << wo).many().optional() + << st("}") ) yield @@ -1095,7 +1096,7 @@ def macro_call(): .then(whitespace) .then(regex(r"\@*[a-zA-Z_][a-zA-Z_0-9]*").desc("injected macro name")) .skip(wo), - (s("(") >> expr_argument_list.desc("macro arguments") << s(")")) + (st("(") >> expr_argument_list.desc("macro arguments") << st(")")) .optional() .map(lambda x: x or []), ) @@ -1104,7 +1105,7 @@ def macro_call(): @generate def state(): - return state_no_colon << s(";") + return state_no_colon << st(";") yield @@ -1253,16 +1254,16 @@ def modifier_selector_basic(): # state selector in a modifier return ( - (ist("flag") >> wo >> s("(") >> state_modifier_name << s(")")).map( + (ist("flag") >> wo >> st("(") >> state_modifier_name << st(")")).map( selector_flag ) - | (ist("sprite") >> wo >> s("(") >> sprite_name << s(")")).map(selector_name) + | (ist("sprite") >> wo >> st("(") >> sprite_name << st(")")).map(selector_name) | ( ist("duration") >> wo - >> s("(") + >> st("(") >> regex(r"\d+").optional().map(lambda x: int(x) if x else 0) - << s(")") + << st(")") ).map(selector_duration) ) yield @@ -1280,7 +1281,7 @@ def modifier_selector_expr(): lambda a: (lambda code, ctx, state: not a(code, ctx, state)) ) | ( - s("(") + st("(") >> ( ( ( @@ -1325,7 +1326,7 @@ def modifier_selector_expr(): ) | modifier_selector_expr.skip(wo) ) - << s(")") + << st(")") ) ) yield @@ -1339,10 +1340,10 @@ def modifier_clause(): ( modifier_effect.map(lambda e: [e]) | ( - s("{") + st("{") >> wo - >> (modifier_effect << wo << s(";").optional() << wo).at_least(1) - << s("}") + >> (modifier_effect << wo << st(";").optional() << wo).at_least(1) + << st("}") ) ) << wo, @@ -1354,11 +1355,11 @@ def modifier_clause(): def mod_block_body(): return modifier_clause.map(lambda a: [a]) | ( wo - >> s("{") + >> st("{") >> wo - >> ((modifier_clause << wo << s(";").optional()).many()) + >> ((modifier_clause << wo << st(";").optional()).many()) << wo - << s("}") + << st("}") ) yield @@ -1374,10 +1375,10 @@ def state_body(): @generate def sometimes_statement(): return seq( - s("sometimes") + st("sometimes") >> whitespace >> ( - s("(") >> wo >> expression << wo << s(")") + st("(") >> wo >> expression << wo << st(")") | replaceable_number.map( lambda r: ( "expr", @@ -1390,7 +1391,7 @@ def sometimes_statement(): ) ) ).tag("chance") - << s("%").optional() + << st("%").optional() << wo, state_body.optional().map(lambda x: x if x is not None else []).tag("body"), ) @@ -1431,10 +1432,10 @@ def if_statement(): .skip(string(")")) .skip(wo), state_body.optional().map(lambda x: x if x is not None else []).skip(wo), - s(";") + st(";") .optional() .then(wo) - .then(s("else")) + .then(st("else")) .then(wo) .then(state_body.optional().map(lambda x: x if x is not None else [])) .skip(wo) @@ -1506,10 +1507,10 @@ def ifjump_statement(): .then(state_call) .skip(wo), state_body.optional().map(lambda x: x if x is not None else []).skip(wo), - s(";") + st(";") .optional() .then(wo) - .then(s("else")) + .then(st("else")) .then(wo) .then(state_body.optional().map(lambda x: x if x is not None else [])) .skip(wo) @@ -1527,10 +1528,10 @@ def whilejump_statement(): .then(state_call) .skip(wo), state_body.optional().map(lambda x: x if x is not None else []).skip(wo), - s(";") + st(";") .optional() .then(wo) - .then(s("else")) + .then(st("else")) .then(wo) .then(state_body.optional().map(lambda x: x if x is not None else [])) .skip(wo) @@ -1552,10 +1553,10 @@ def while_statement(): .skip(string(")")) .skip(wo), state_body.optional().map(lambda x: x if x is not None else []).skip(wo), - s(";") + st(";") .optional() .then(wo) - .then(s("else")) + .then(st("else")) .then(wo) .then(state_body.optional().map(lambda x: x if x is not None else [])) .skip(wo) @@ -1586,7 +1587,7 @@ def repeat_statement(): def anonymous_macro(): return seq( ist("macro") - >> (s("(") >> macro_argument_list << s(")") << wo) + >> (st("(") >> macro_argument_list << st(")") << wo) .optional() .map(lambda a: a or []), state_body, @@ -1601,7 +1602,7 @@ def macro_def(): ist("macro") >> whitespace >> regex(r"[a-zA-Z_][a-zA-Z_0-9]*").desc("macro name").tag("name"), - (wo >> s("(") >> macro_argument_list << s(")") << wo) + (wo >> st("(") >> macro_argument_list << st(")") << wo) .optional() .map(lambda a: a or []) .tag("args"), @@ -1648,7 +1649,7 @@ def recursive_paren_content(): return ( ( (regex(r"[^\(\)]") if i else regex(r"[^\(\)\,]")) - | (s("(") + rpcont(i + 1)) + s(")") + | (st("(") + rpcont(i + 1)) + st(")") ) .many() .concat() @@ -1659,35 +1660,37 @@ def recursive_paren_content(): def preprocess_for_macros(code, defines=(), this_fname=None, i=0): - l = code + curr_line = code for key, (val, d_args) in defines.items(): - nl = "" + new_lines = "" while True: - j = re.search(r"\b" + re.escape(key.upper()) + r"\(", l.upper()) + macro_pos = re.search( + r"\b" + re.escape(key.upper()) + r"\(", curr_line.upper() + ) - if not j: - nl += l + if not macro_pos: + new_lines += curr_line break - j = j.start() - nl += l[:j] - l = l[j + len(key) :] + macro_pos = macro_pos.start() + new_lines += curr_line[:macro_pos] + curr_line = curr_line[macro_pos + len(key) :] try: - args, l = ( - s("(") >> rpcont().sep_by(s(",") << wo) << s(")") - ).parse_partial(l) + args, curr_line = ( + st("(") >> rpcont().sep_by(st(",") << wo) << st(")") + ).parse_partial(curr_line) args = [x for x in args if x] - except parsy.ParseError as e: + except parsy.ParseError as err: print("\n") traceback.print_exc() print() raise PreprocessingError( - f"Unexpected parse error using parametrized preprocessor alias '{key}' ({e})", + f"Unexpected parse error using parametrized preprocessor alias '{key}' ({err})", i, this_fname, code + "\n\n", @@ -1701,18 +1704,15 @@ def preprocess_for_macros(code, defines=(), this_fname=None, i=0): code, ) - v = val - for aname, aval in zip(d_args, args): - # ov = v - v = re.sub(r"\({}\)".format(re.escape(aname)), f"({aval})", v) + val = re.sub(r"\({}\)".format(re.escape(aname)), f"({aval})", val) - res = preprocess_for_macros(v, defines, this_fname, i) - nl += res + res = preprocess_for_macros(val, defines, this_fname, i) + new_lines += res - l = nl + curr_line = new_lines - return l + return curr_line def preprocess_code( @@ -1905,9 +1905,9 @@ def preprocess_code( (key, args), _ = seq( regex(r"[a-zA-Z_][a-zA-Z0-9_]*"), ( - s("(") - >> (regex(r"[a-zA-Z_][a-zA-Z0-9_]*").sep_by(s(","))) - << s(")") + st("(") + >> (regex(r"[a-zA-Z_][a-zA-Z0-9_]*").sep_by(st(","))) + << st(")") ) .optional() .map(lambda x: x or []), diff --git a/zdcode/compiler/task.py b/zdcode/compiler/task.py index 1593342..7b309f1 100644 --- a/zdcode/compiler/task.py +++ b/zdcode/compiler/task.py @@ -1,3 +1,4 @@ +"""Pending tasks in the compiler.""" import functools diff --git a/zdcode/objects/__init__.py b/zdcode/objects/__init__.py index 4b8fbcc..55f818b 100644 --- a/zdcode/objects/__init__.py +++ b/zdcode/objects/__init__.py @@ -1,18 +1 @@ """ZDCode objects.""" - -from .actor import ZDActor -from .actor import ZDBaseActor -from .block import ZDBlock -from .dummy import ZDDummyActor -from .dummy import ZDDummyLabel -from .ifjump import ZDIfJumpStatement -from .ifs import ZDIfStatement -from .label import ZDLabel -from .modclause import ZDModClause -from .property import ZDProperty -from .raw import ZDRawDecorate -from .state import ZDState -from .state import zerotic -from .template import ZDClassTemplate -from .whilejump import ZDWhileJumpStatement -from .whiles import ZDWhileStatement diff --git a/zdcode/objects/actor.py b/zdcode/objects/actor.py index 9de0771..bca2173 100644 --- a/zdcode/objects/actor.py +++ b/zdcode/objects/actor.py @@ -1,9 +1,11 @@ +"""The DECORATE actor class and related classes.""" from typing import TYPE_CHECKING from typing import Iterable from .. import _user_array_setters from .. import _user_var_setters from ..compiler.context import ZDCodeParseContext +from ..types.basic import ZDObject from ..types.basic import ZDStateObject from ..util import TextNode from ..util import decorate @@ -17,7 +19,7 @@ from ..compiler.compiler import ZDCode -class ZDBaseActor(object): +class ZDBaseActor: """A basic actor-like class.""" def __init__( @@ -34,10 +36,10 @@ def __init__( self.inherit = inherit self.replace = replace self.num = doomednum - self.id = _id or make_id(30) + self.identifier = _id or make_id(30) -class ZDActor(ZDBaseActor): +class ZDActor(ZDBaseActor, ZDObject): """A DECORATE class.""" def __init__( @@ -78,7 +80,11 @@ def __init__( # code.actor_names[name.upper()] = self def __repr__(self) -> str: - return f"" + return ( + f"" + ) def get_context(self) -> ZDCodeParseContext: """Returns the [ZDCodeParseContext] of this Actor.""" @@ -110,18 +116,22 @@ def _set_user_var_state(self, var) -> Iterable[ZDState]: if vtype == "val": return [ ZDState( - action=f"{_user_var_setters[var['type']]}({stringify(var['name'])}, {vlit})" + action=f"{_user_var_setters[var['type']]}" + f"({stringify(var['name'])}, {vlit})" ) ] - elif vtype == "arr": + if vtype == "arr": return [ ZDState( - action=f"{_user_array_setters[var['type']]}({stringify(var['name'])}, {i}, {v})" + action=f"{_user_array_setters[var['type']]}" + f"({stringify(var['name'])}, {i}, {v})" ) for i, v in enumerate(vlit) ] + raise ValueError(f"Unknown user var type: {vtype}") + def _get_spawn_prelude(self) -> Iterable[ZDStateObject]: """Returns the prelude of the Spawn funtion.""" return sum( @@ -149,35 +159,30 @@ def top(self): The top holds every property, flag, combo, and user variable declaration in the DECORATE class. """ - r = TextNode() + text = TextNode() - for p in sorted(self.properties, key=lambda p: p.name): - r.add_line(decorate(p)) + for prop in sorted(self.properties, key=lambda prop: prop.name): + text.add_line(decorate(prop)) - r.add_line("") + text.add_line("") - for u in self.uservars: - r.add_line( - "var {} {}{};".format( - u["type"], - u["name"], - "[{}]".format(u["size"]) if u.get("size", 0) else "", - ) - ) + for user_var in self.uservars: + array_suffix = f"[{user_var['size']}]" if user_var.get("size", 0) else "" + text.add_line(f"var {user_var['type']} {user_var['name']}{array_suffix};") - for f in self.flags: - r.add_line(f"+{f}") + for flag in self.flags: + text.add_line(f"+{flag}") - for a in self.antiflags: - r.add_line(f"-{a}") + for antiflag in self.antiflags: + text.add_line(f"-{antiflag}") - for rd in self.raw: - r.add_line(rd) + for verbatim in self.raw: + text.add_line(verbatim) - if len(r) == 1 and r[0].strip() == "": + if len(text) == 1 and text[0].strip() == "": return " " - return r + return text def transform_spawn(self, label: ZDLabel) -> ZDLabel: """Transforms the Spawn label, making it spawn safe.""" @@ -203,18 +208,18 @@ def label_code(self) -> TextNode: Any code that isn't the class declaration or the top is considered 'label code'. """ - r = TextNode() + text = TextNode() - for f in self.funcs: - r.add_line(decorate(f[1])) + for func in self.funcs: + text.add_line(decorate(func[1])) for label in self.labels: if label.name.upper() == "SPAWN": label = self.transform_spawn(label) - r.add_line(decorate(label)) + text.add_line(decorate(label)) - return r + return text def header(self) -> str: """Returns the header of the class. @@ -223,16 +228,16 @@ def header(self) -> str: DECCORATE. It contains informatino such as inheritancce, replacement, and the editor number (DoomEdNum). """ - r = self.name + text = self.name if self.inherit: - r += f" : {self.inherit}" + text += f" : {self.inherit}" if self.replace: - r += f" replaces {self.replace}" + text += f" replaces {self.replace}" if self.num: - r += f" {str(self.num)}" + text += f" {str(self.num)}" - return r + return text def to_decorate(self) -> TextNode: if self.labels + self.funcs: diff --git a/zdcode/objects/block.py b/zdcode/objects/block.py index d5ce9a8..66eb830 100644 --- a/zdcode/objects/block.py +++ b/zdcode/objects/block.py @@ -1,3 +1,4 @@ +"""The ZDCode state container.""" from typing import Generator from typing import Iterable from typing import Self diff --git a/zdcode/objects/dummy.py b/zdcode/objects/dummy.py index 9684f0e..717aa8c 100644 --- a/zdcode/objects/dummy.py +++ b/zdcode/objects/dummy.py @@ -1,3 +1,4 @@ +"""Dummy objects, for internal use only.""" from ..compiler.context import ZDCodeParseContext from ..types.basic import ZDObject from ..types.basic import ZDStateObject @@ -5,7 +6,7 @@ from .actor import ZDBaseActor -class ZDDummyActor(ZDObject): +class ZDDummyActor(ZDBaseActor, ZDObject): """A dummy actor. Used in internal logic only - cannot be compiled to DECORATE.""" @@ -43,13 +44,18 @@ def __repr__(self): return "[dummy state]" def label_name(self) -> str: + """Returns the name of this label.""" raise NotImplementedError def to_decorate(self) -> TextNode: raise NotImplementedError def get_context(self) -> None: + """Returns the context of this label. + + Since this is a dummy label. returns simply None.""" return None def add_state(self, state: ZDStateObject) -> None: + """Adds a state to this label.""" self.states.append(state) diff --git a/zdcode/objects/ifjump.py b/zdcode/objects/ifjump.py index 0ff42d6..6c321b8 100644 --- a/zdcode/objects/ifjump.py +++ b/zdcode/objects/ifjump.py @@ -47,10 +47,18 @@ def state_containers(self) -> Generator[Iterable[ZDStateObject], None, None]: yield from self.else_block.state_containers() def set_else(self, else_block: Iterable[ZDStateObject] | None): + """Sets a state object or container to act as the 'else' block. + + Passing None unsets the else block.""" self.else_block = else_block @classmethod def generate(cls, actor: "ZDActor", states: Iterable[ZDStateObject] | None = ()): + """Decorates a function to produce an ifjump block. + + The decorated function is used to generate the code for the condition check; + that is, the jump itself.""" + def _decorator(condition_gen): return cls(actor, condition_gen, states) @@ -69,8 +77,7 @@ def num_states(self) -> int: if self.else_block: return self.num_block_states() + self.num_else_states() + 3 - else: - return self.num_block_states() + 3 + return self.num_block_states() + 3 def to_decorate(self) -> TextNode: num_st_bl = self.num_block_states() @@ -88,12 +95,11 @@ def to_decorate(self) -> TextNode: ] ) - else: - return TextNode( - [ - f"{zerotic} {self.true_condition(2)}", - f"{zerotic} A_Jump(256, {num_st_bl + 1})\n", - TextNode([x.to_decorate() for x in self.states]), - zerotic, - ] - ) + return TextNode( + [ + f"{zerotic} {self.true_condition(2)}", + f"{zerotic} A_Jump(256, {num_st_bl + 1})\n", + TextNode([x.to_decorate() for x in self.states]), + zerotic, + ] + ) diff --git a/zdcode/objects/ifs.py b/zdcode/objects/ifs.py index a846f80..cd63735 100644 --- a/zdcode/objects/ifs.py +++ b/zdcode/objects/ifs.py @@ -1,3 +1,4 @@ +"""The ZDCode if statement.""" from typing import Generator from typing import Iterable from typing import Self @@ -43,7 +44,9 @@ def state_containers(self) -> Generator[Iterable[ZDStateObject], None, None]: yield from self.else_block.state_containers() def set_else(self, else_block: ZDStateObject): - """Set a state object or container to act as an 'else' block.""" + """Sets a state object or container to act as the 'else' block. + + Passing None unsets the else block.""" self.else_block = else_block def num_block_states(self): @@ -61,8 +64,7 @@ def num_states(self): if self.else_block: return self.num_block_states() + self.num_else_states() + 3 - else: - return self.num_block_states() + 2 + return self.num_block_states() + 2 def to_decorate(self) -> TextNode: num_st_bl = self.num_block_states() @@ -80,11 +82,10 @@ def to_decorate(self) -> TextNode: ] ) - else: - return TextNode( - [ - f"{zerotic} A_JumpIf(!({self.true_condition}), {num_st_bl + 1})\n", - TextNode([x.to_decorate() for x in self.states]), - zerotic, - ] - ) + return TextNode( + [ + f"{zerotic} A_JumpIf(!({self.true_condition}), {num_st_bl + 1})\n", + TextNode([x.to_decorate() for x in self.states]), + zerotic, + ] + ) diff --git a/zdcode/objects/raw.py b/zdcode/objects/raw.py index 7dd0b4e..e8d9d16 100644 --- a/zdcode/objects/raw.py +++ b/zdcode/objects/raw.py @@ -1,3 +1,4 @@ +"""The ZDCode verbatim text object. For internal use only.""" from typing import Generator from typing import Iterable @@ -6,6 +7,8 @@ class ZDRawDecorate(ZDStateObject): + """Verbatim text, to be pasted directly. Internal use only.""" + def __init__(self, raw: str, num_states: int = 0): self.raw = raw self._num_states = num_states diff --git a/zdcode/objects/skip.py b/zdcode/objects/skip.py index 27fe87c..6558a4c 100644 --- a/zdcode/objects/skip.py +++ b/zdcode/objects/skip.py @@ -1,3 +1,5 @@ +"""The skip object.""" + from typing import TYPE_CHECKING from typing import Generator from typing import Iterable @@ -8,17 +10,24 @@ if TYPE_CHECKING: from ..compiler.compiler import ZDCode + from ..compiler.context import ZDCodeParseContext + +class ZDSkip(ZDStateObject): + """A state skip. For internal use only.""" -class ZDSkip: - def __init__(self, code: "ZDCode", skip_context, curr_ind): + def __init__( + self, code: "ZDCode", skip_context: "ZDCodeParseContext", curr_ind: int + ): self.code = code self.context = skip_context self.ind = curr_ind def clone(self) -> Self: raise NotImplementedError( - "State group skipping (e.g. return in macros) could not be cloned. Injected macros cannot be cloned - please don't inject them until all else is resolved!" + "State group skipping (e.g. return in macros) could not be cloned. " + "Injected macros cannot be cloned - please don't inject them until " + "all else is resolved!" ) def state_containers(self) -> Generator[Iterable[ZDStateObject], None, None]: @@ -31,5 +40,5 @@ def spawn_safe(self): def to_decorate(self): return f"{zerotic} A_Jump(256, {self.context.remote_num_states() - self.ind})" - def num_states(self): + def num_states(self) -> int: return 1 diff --git a/zdcode/objects/sometimes.py b/zdcode/objects/sometimes.py index 5463b44..012cda3 100644 --- a/zdcode/objects/sometimes.py +++ b/zdcode/objects/sometimes.py @@ -1,3 +1,4 @@ +"""The ZDCode sometimes statement.""" from typing import Generator from typing import Iterable from typing import Self diff --git a/zdcode/objects/state.py b/zdcode/objects/state.py index a617e75..48dfc98 100644 --- a/zdcode/objects/state.py +++ b/zdcode/objects/state.py @@ -1,3 +1,4 @@ +"""The DECORATE state, and related utilities.""" from typing import Generator from typing import Iterable from typing import Self @@ -66,7 +67,8 @@ def to_decorate(self) -> TextNode: return TextNode( [ - f"{self.sprite.upper()} {self.frame.upper()} {str(self.duration)}{' '.join(keywords)}{action}" + f"{self.sprite.upper()} {self.frame.upper()} {str(self.duration)}" + f"{' '.join(keywords)}{action}" ] ) @@ -74,8 +76,10 @@ def __str__(self): return str(self.to_decorate()) def __repr__(self): - return f"" + return ( + f"" + ) zerotic = ZDState.zerotic() -zerotic = ZDState.zerotic() diff --git a/zdcode/objects/template.py b/zdcode/objects/template.py index 69c7795..5c838bf 100644 --- a/zdcode/objects/template.py +++ b/zdcode/objects/template.py @@ -1,22 +1,43 @@ +"""A ZDCode class template.""" import hashlib import itertools +from typing import TYPE_CHECKING from typing import Iterable +from typing import TypedDict from ..compiler.error import CompilerError from ..util import make_id from ..util import stringify from .actor import ZDActor from .actor import ZDBaseActor +from .actor import ZDObject +if TYPE_CHECKING: + from ..compiler.compiler import ZDCode + from ..compiler.context import ZDCodeParseContext + + +class UserArrayDefinition(TypedDict): + """An internal object defining an abstract user array. + + Used in class templates only to allow overriding an array used by the template in + the derivation.""" + + name: str + size: str + type: str + + +class ZDClassTemplate(ZDBaseActor, ZDObject): + """A ZDCode class template.""" -class ZDClassTemplate(ZDBaseActor): def __init__( self, template_parameters: Iterable[str], parse_data, abstract_label_names: Iterable[str], abstract_macro_names: dict[str, Iterable[str]], - abstract_array_names: dict[str, {"size": int, "type": str}], + abstract_array_names: dict[str, UserArrayDefinition], group_name: str | None, code: "ZDCode", name: str, @@ -45,6 +66,7 @@ def duplicate( provided_macro_names: Iterable[str], provided_array_names: Iterable[str], ): + """Duplicates this class template.""" if ( self.abstract_macro_names or self.abstract_label_names @@ -69,6 +91,7 @@ def which_duplicate( provided_macro_names: Iterable[str], provided_array_names: Iterable[str], ): + """Find a class template with these exact parameter definitions.""" return self.existing[ self.parameter_hash( parameter_values, @@ -86,6 +109,7 @@ def register( provided_array_names: Iterable[str], result, ): + """Register the hash of these parameter definitions.""" self.existing[ self.parameter_hash( parameter_values, @@ -102,77 +126,70 @@ def parameter_hash( provided_macro_names: Iterable[str], provided_array_names: Iterable[str], ) -> str: - hash = hashlib.sha256() + """Hashes parameter definitions.""" + hashed = hashlib.sha256() - hash.update(self.name.encode("utf-8")) - hash.update(b"|") + hashed.update(self.name.encode("utf-8")) + hashed.update(b"|") - hash.update(self.id.encode("utf-8")) - hash.update(b"|") + hashed.update(self.identifier.encode("utf-8")) + hashed.update(b"|") if ( self.abstract_macro_names or self.abstract_label_names or self.abstract_array_names ): - hash.update(make_id(80).encode("utf-8")) + hashed.update(make_id(80).encode("utf-8")) else: for parm in parameter_values: - hash.update(parm.encode("utf-8")) - hash.update(b"-") + hashed.update(parm.encode("utf-8")) + hashed.update(b"-") - hash.update(b"|") + hashed.update(b"|") for name in itertools.chain(provided_label_names, provided_array_names): - hash.update(name.encode("utf-8")) - hash.update(b"-") + hashed.update(name.encode("utf-8")) + hashed.update(b"-") - hash.update(b"|") + hashed.update(b"|") for name, args in provided_macro_names.items(): - hash.update(hex(len(args)).encode("utf-8")) - hash.update(name.encode("utf-8")) - hash.update(b"-") + hashed.update(hex(len(args)).encode("utf-8")) + hashed.update(name.encode("utf-8")) + hashed.update(b"-") - return hash.hexdigest() + return hashed.hexdigest() - def generated_class_name( + def generate_class_name( self, parameter_values, provided_label_names: Iterable[str], provided_macro_names: Iterable[str], provided_array_names: Iterable[str], ): - hash = self.parameter_hash( + """Generates a class name for an anonymous class template.""" + hashed = self.parameter_hash( parameter_values, provided_label_names, provided_macro_names, provided_array_names, ) - return f"{self.name}__deriv_{hash}" - - def assert_group_exists( - self, groupname: str, ctx_str: str, context: "ZDCodeParseContext" - ): - if groupname not in self.code.groups: - raise CompilerError( - f"No such group '{groupname}' {ctx_str} (in f{context.describe()})!" - ) + return f"{self.name}__deriv_{hashed}" def generate_init_class( self, - code: "ZDCode", context: "ZDCodeParseContext", parameter_values, provided_label_names: Iterable[str] = (), provided_macro_names: Iterable[str] = (), provided_array_names: Iterable[str] = (), name: str | None = None, - pending: str | None = None, inherits: str | None = None, group: str | None = None, ): + """Generates an initial ZDCode class object from this template.""" provided_label_names = set(provided_label_names) provided_macro_names = dict(provided_macro_names) @@ -192,7 +209,7 @@ def generate_init_class( new_name = ( name if name is not None - else self.generated_class_name( + else self.generate_class_name( parameter_values, provided_label_names, provided_macro_names, @@ -201,15 +218,16 @@ def generate_init_class( ) ctx_str = ( - f"in {name and 'derivation ' + name or 'anonymous derivation'} of {self.name}", + f"in {name and 'derivation ' + name or 'anonymous derivation'} " + "of {self.name}", ) if self.group_name: - self.assert_group_exists(self.group_name, ctx_str, context) + self.code.assert_group_exists(self.group_name, ctx_str, context) self.code.groups[self.group_name].append(stringify(new_name)) if group and group != self.group_name: - self.assert_group_exists(group, ctx_str, context) + self.code.assert_group_exists(group, ctx_str, context) self.code.groups[group].append(stringify(new_name)) context_new = context.derive(f"derivation of template {self.name}") @@ -231,32 +249,44 @@ def generate_init_class( res = ZDActor(self.code, new_name, inh, rep, self.num, context=context_new) - for l in self.abstract_label_names: - if l not in provided_label_names: + for label_name in self.abstract_label_names: + if label_name not in provided_label_names: raise CompilerError( - f"Tried to derive template {self.name} in {context.describe()}, but abstract label {l} does not have a definition!" + f"Tried to derive template {self.name} in {context.describe()}, " + f"but abstract label {label_name} does not have a definition!" ) - for m, a in self.abstract_macro_names.items(): - if m not in provided_macro_names.keys(): + for macro_name, macro_defs in self.abstract_macro_names.items(): + if macro_name not in provided_macro_names: raise CompilerError( - f"Tried to derive template {self.name} in {context.describe()}, but abstract macro {m} does not have a definition!" + f"Tried to derive template {self.name} in {context.describe()}, " + f"but abstract macro {macro_name} does not have a definition!" ) - if len(a) != len(provided_macro_names[m]): + if len(macro_defs) != len(provided_macro_names[macro_name]): raise CompilerError( - f"Tried to derive template {self.name} in {context.describe()}, but abstract macro {m} has the wrong number of arguments: expected {len(a)}, got {len(provided_macro_names[m])}!" + f"Tried to derive template {self.name} in {context.describe()}, " + f"but abstract macro {macro_name} has the " + f"wrong number of arguments: expected {len(macro_defs)}, " + f"got {len(provided_macro_names[macro_name])}!" ) - for m, a in self.abstract_array_names.items(): - if m not in provided_array_names.keys(): + for macro_name, macro_defs in self.abstract_array_names.items(): + if macro_name not in provided_array_names: raise CompilerError( - f"Tried to derive template {self.name} in {context.describe()}, but abstract array {m} is not defined!" + f"Tried to derive template {self.name} in {context.describe()}, " + f"but abstract array {macro_name} is not defined!" ) - if a["size"] != "any" and a["size"] != provided_array_names[m]: + if ( + macro_defs["size"] != "any" + and macro_defs["size"] != provided_array_names[macro_name] + ): raise CompilerError( - f"Tried to derive template {self.name} in {context.describe()}, but abstract array {m} has a size constraint; expected {a['size']} array elements, got {provided_array_names[m]}!" + f"Tried to derive template {self.name} in {context.describe()}, " + f"but abstract array {macro_name} has a size constraint; " + f"expected {macro_defs['size']} array elements, " + f"got {provided_array_names[macro_name]}!" ) self.register( @@ -273,6 +303,7 @@ def generate_init_class( return True, res def get_init_replacements(self, parameter_values): + """Corresponds the list of values to the template's parameters.""" return dict( zip((p.upper() for p in self.template_parameters), parameter_values) ) diff --git a/zdcode/objects/whilejump.py b/zdcode/objects/whilejump.py index 48cfdf6..d9ea4c9 100644 --- a/zdcode/objects/whilejump.py +++ b/zdcode/objects/whilejump.py @@ -1,19 +1,19 @@ """The ZDCode whilejump statement.""" -from typing import TYPE_CHECKING from typing import Callable from typing import Generator from typing import Iterable from typing import Self +from ..types.basic import ZDStateContainer from ..types.basic import ZDStateObject from ..util import TextNode from ..util import stringify from .actor import ZDActor from .state import zerotic -from .whiles import num_whiles +from .whiles import ZDWhileStatement -class ZDWhileJumpStatement(object): +class ZDWhileJumpStatement(ZDStateContainer): """The ZDCode whilejump statement. Allows any conditional jump state action (such as A_JumpIfTargetCloser) to be used @@ -27,10 +27,8 @@ def __init__( self.states: list[ZDStateObject] = list(states) self.else_block = None - global num_whiles - - self._while_id = num_whiles - num_whiles += 1 + self._while_id = ZDWhileStatement.num_whiles + ZDWhileStatement.num_whiles += 1 self._loop_id = "_loop_while_" + str(self._while_id) @@ -49,27 +47,37 @@ def state_containers(self) -> Generator[Iterable[ZDStateObject], None, None]: yield from self.else_block.state_containers() def set_else(self, else_block): + """Sets a state object or container to act as the 'else' block. + + Passing None unsets the else block.""" self.else_block = else_block @classmethod def generate(cls, actor, states=()): + """Decorate a function to produce a while jump statement. + + Uses the decorated function to produce the code for the condition check.""" + def _decorator(condition_gen): return cls(actor, condition_gen, states) return _decorator def num_block_states(self): + """Returns the number of states in the block. + + Does not count the else block, if applicable.""" return sum(x.num_states() for x in self.states) def num_else_states(self): + """Returns the number of states in the else block only.""" return self.else_block.num_states() def num_states(self): if self.else_block: return self.num_block_states() + self.num_else_states() + 4 - else: - return self.num_block_states() + 4 + return self.num_block_states() + 4 def spawn_safe(self): return False @@ -92,14 +100,13 @@ def to_decorate(self): ] ) - else: - return TextNode( - [ - f"{zerotic} {self.true_condition(2)}", - f"{zerotic} A_Jump(256, {num_st_bl + 2})", - f"{self._loop_id}:", - TextNode([x.to_decorate() for x in self.states]), - f"{zerotic} {self.true_condition(stringify(self._loop_id))}", - zerotic, - ] - ) + return TextNode( + [ + f"{zerotic} {self.true_condition(2)}", + f"{zerotic} A_Jump(256, {num_st_bl + 2})", + f"{self._loop_id}:", + TextNode([x.to_decorate() for x in self.states]), + f"{zerotic} {self.true_condition(stringify(self._loop_id))}", + zerotic, + ] + ) diff --git a/zdcode/objects/whiles.py b/zdcode/objects/whiles.py index 144ebfa..596334b 100644 --- a/zdcode/objects/whiles.py +++ b/zdcode/objects/whiles.py @@ -1,3 +1,4 @@ +"""The ZDCode while statement.""" from typing import Generator from typing import Iterable from typing import Self @@ -8,24 +9,22 @@ from ..util import stringify from .state import zerotic -num_whiles = 0 - class ZDWhileStatement(ZDStateContainer): - """A ZDCode while statemet. + """A ZDCode while statement. Uses jumps to run a segment of DECORATE states until a condition becomes true.""" + num_whiles = 0 + def __init__(self, actor, condition, states=()): self._actor = actor self.true_condition = condition self.states: list[ZDStateObject] = list(states) self.else_block = None - global num_whiles - - self._while_id = num_whiles - num_whiles += 1 + self._while_id = ZDWhileStatement.num_whiles + ZDWhileStatement.num_whiles += 1 self._loop_id = "_loop_while_" + str(self._while_id) @@ -44,20 +43,26 @@ def state_containers(self) -> Generator[Iterable[ZDStateObject], None, None]: yield from self.else_block.state_containers() def set_else(self, else_block): + """Sets a state object or container to act as the 'else' block. + + Passing None unsets the else block.""" self.else_block = else_block - def num_block_states(self): + def num_block_states(self) -> int: + """Returns the number of states in the block. + + Does not count the else block if there is one.""" return sum(x.num_states() for x in self.states) - def num_else_states(self): - return self.else_block.num_states() + def num_else_states(self) -> int: + """Returns the number of states in the else block only.""" + return self.else_block and self.else_block.num_states() or 0 - def num_states(self): + def num_states(self) -> int: if self.else_block: return self.num_block_states() + self.num_else_states() + 4 - else: - return self.num_block_states() + 3 + return self.num_block_states() + 3 def spawn_safe(self): return False @@ -75,18 +80,23 @@ def to_decorate(self): f"{zerotic} A_Jump(256, {num_st_bl + 2})", f"{self._loop_id}:", TextNode([x.to_decorate() for x in self.states]), - f"{zerotic} A_JumpIf({self.true_condition}, {stringify(self._loop_id)})", + ( + f"{zerotic} A_JumpIf({self.true_condition}, " + f"{stringify(self._loop_id)})" + ), zerotic, ] ) - else: - return TextNode( - [ - f"{zerotic} A_JumpIf(!({self.true_condition}), {num_st_bl + 2})", - f"{self._loop_id}:", - TextNode([x.to_decorate() for x in self.states]), - f"{zerotic} A_JumpIf({self.true_condition}, {stringify(self._loop_id)})", - zerotic, - ] - ) + return TextNode( + [ + f"{zerotic} A_JumpIf(!({self.true_condition}), {num_st_bl + 2})", + f"{self._loop_id}:", + TextNode([x.to_decorate() for x in self.states]), + ( + f"{zerotic} A_JumpIf({self.true_condition}, " + f"{stringify(self._loop_id)})" + ), + zerotic, + ] + ) diff --git a/zdcode/types/__init__.py b/zdcode/types/__init__.py index 8b13789..2e3e3b2 100644 --- a/zdcode/types/__init__.py +++ b/zdcode/types/__init__.py @@ -1 +1 @@ - +"""ZDCode type hinting facilities, like interfaces.""" diff --git a/zdcode/types/basic.py b/zdcode/types/basic.py index aaccf80..cc4059b 100644 --- a/zdcode/types/basic.py +++ b/zdcode/types/basic.py @@ -1,3 +1,4 @@ +"""Basic type interfaces for ZDCode.""" from typing import TYPE_CHECKING from typing import Generator from typing import Iterable @@ -15,13 +16,11 @@ class ZDObject(Protocol): def to_decorate(self) -> str: """Cconverts this ZDCode object to DECORATE.""" - ... def get_context(self) -> Union["ZDCodeParseContext", None]: """Returns the context of this ZDObject. If not applicable, returns None.""" - ... class ZDStateObject(ZDObject, Protocol): @@ -29,25 +28,24 @@ class ZDStateObject(ZDObject, Protocol): def clone(self) -> Self: """Clone this state object.""" - ... def spawn_safe(self) -> bool: """Whether this collection is 'spawn safe'. - Spawn safety denotes a state which can be used at the start of a Spawn label without causing issues. Non-spawn-safe Spawn labels will be automatically padded with TNT1 A 0 to prevent issues. + Spawn safety denotes a state which can be used at the start of a Spawn label + without causing issues. Non-spawn-safe Spawn labels will be automatically padded + with TNT1 A 0 to prevent issues. """ - ... def num_states(self) -> int: """The number of states in this state object.""" - ... def state_containers(self) -> Generator[Iterable["ZDStateObject"], None, None]: """A list of mutable state containers with [ZDStateObject]s. - Some of these are references to this data structure or children thereof. Others are constructed on the fly for the sake of interface compatibility. + Some of these are references to this data structure or children thereof. Others + are constructed on the fly for the sake of interface compatibility. """ - ... class ZDStateContainer(ZDStateObject, Protocol): @@ -55,4 +53,3 @@ class ZDStateContainer(ZDStateObject, Protocol): def add_state(self, state: ZDStateObject) -> None: """Adds a ZDCode state object to this container.""" - ... diff --git a/zdcode/zake.py b/zdcode/zake.py index cc1f150..8a40560 100644 --- a/zdcode/zake.py +++ b/zdcode/zake.py @@ -63,6 +63,159 @@ def add_target(self, name: str) -> ZakeTarget: """Creates and registers a target, before returning it.""" return self.targets.setdefault(name, ZakeTarget(name)) + def process_matchers_and_excluders( + self, + targ, + bundle, + s_bundle_cfg, + matchers_name, + excluders_name, + excluders_global_name, + get_bundle_cfg, + ): + """Process matchers and excluder in the config for a given bundle.""" + matchers = get_bundle_cfg(s_bundle_cfg, matchers_name) + excluders = get_bundle_cfg(s_bundle_cfg, excluders_name) + excluders_global = get_bundle_cfg(s_bundle_cfg, excluders_global_name) + + if matchers: + for matcher in matchers.strip().split(): + targ.outputs[bundle].add_matcher(matcher) + + if excluders: + for excluder in excluders.strip().split(): + targ.outputs[bundle].add_excluder(excluder) + + if excluders_global: + for excluder in excluders_global.strip().split(): + targ.outputs[bundle].add_excluder(excluder) + + def process_config_bundle( + self, targ, bundle, config, s_bundle_cfg, interp, pats, get_bundle_cfg + ): + """Process the configuration for a given bundle.""" + out_name = "output." + bundle + matchers_name = "matchers." + bundle + excluders_name = "excluders." + bundle + excluders_global_name = "excluders" + priority_name = "priority." + bundle + + priority = config.getfloat( + s_bundle_cfg, + priority_name, + vars=interp, + fallback=config.getfloat( + "Partition", priority_name, vars=interp, fallback=None + ), + ) + + if priority is not None: + targ.outputs[bundle].priority = priority + + if out_name in pats: + targ.outputs[bundle].output = pats[out_name].strip() + + else: + raise ZakeConfigError( + f"Required Zmatchers_and_excludersake field '{out_name}' missing " + f"from section Paths while reading target {targ.name}." + ) + + self.process_matchers_and_excluders( + targ, + bundle, + s_bundle_cfg, + matchers_name, + excluders_name, + excluders_global_name, + get_bundle_cfg, + ) + + def process_config_injects(self, targ, pats): + """Handles injects in the configuration for a given target.""" + for injs in pats["injects"].strip().split(): + match = self.inj_field_pat.match(injs) + + if not match: + raise ZakeConfigError( + "Malformed value found in given Zake field 'injects': " + injs + ) + + inp, out = match.groups() + targ.add_input((inp, out)) + + def process_targets(self, targs, bundles, tname, interp, config, get_bundle_cfg): + """Process the configuration for a given bundle.""" + targ = self.add_target(tname) + targs[tname] = targ + + c_general = config["General"] + + interp["target"] = tname # for interpolation + interp["defaults"] = "" + + s_pats = "Paths." + tname + s_defs = "Definitions." + tname + s_bundle_cfg = "Partition." + tname + + # define bundle outputs + for bundle in bundles: + targ.outputs[bundle] = BundleOutput( + name=bundle, + output=None, + ) + + if "partitions" not in c_general: + targ.outputs["asset"].priority = 1.0 + targ.outputs["code"].priority = 100.0 + targ.outputs["code"].add_matcher("DECORATE") + targ.outputs["code"].add_matcher("DECORATE*") + + # section values + pats = dict(config.items("Paths", vars=interp)) if "Paths" in config else {} + + defs = ( + dict(config.items("Definitions", vars=interp)) + if "Definitions" in config + else {} + ) + + if s_pats in config: + new_pat_keys = dict(config.items(s_pats, vars=interp)).keys() + + for key in new_pat_keys: + interp["defaults"] = "" + + if key in pats: + interp["defaults"] = pats[key] + + pats[key] = config.get(s_pats, key, vars=interp) + + if s_defs in config: + defs.update(config.items(s_defs, vars=interp)) + + # fetch inputs and outputs + if "inputs" not in pats: + raise ZakeConfigError( + "Required Zake field 'inputs' missing from section Paths " + f"while reading target {tname}." + ) + + for inp in pats["inputs"].strip().split(): + targ.add_input((inp, "")) + + if "injects" in pats: + self.process_config_injects(targ, pats) + + for bundle in bundles: + self.process_config_bundle( + targ, bundle, config, s_bundle_cfg, interp, pats, get_bundle_cfg + ) + + # preprocessor definitions + for name, value in defs.items(): + targ.add_definition(name.strip(), value.strip()) + def read(self, filename, **kwargs): """Read a Zake configuration file.""" config = ConfigParser( @@ -99,122 +252,7 @@ def get_bundle_cfg(s_bundle_cfg, name): # targets for tname in targets: - targ = self.add_target(tname) - targs[tname] = targ - - interp["target"] = tname # for interpolation - interp["defaults"] = "" - - s_pats = "Paths." + tname - s_defs = "Definitions." + tname - s_bundle_cfg = "Partition." + tname - - # define bundle outputs - for bundle in bundles: - targ.outputs[bundle] = BundleOutput( - name=bundle, - output=None, - ) - - if "partitions" not in c_general: - targ.outputs["asset"].priority = 1.0 - targ.outputs["code"].priority = 100.0 - targ.outputs["code"].add_matcher("DECORATE") - targ.outputs["code"].add_matcher("DECORATE*") - - # section values - pats = dict(config.items("Paths", vars=interp)) if "Paths" in config else {} - - defs = ( - dict(config.items("Definitions", vars=interp)) - if "Definitions" in config - else {} - ) - - if s_pats in config: - new_pat_keys = dict(config.items(s_pats, vars=interp)).keys() - - for key in new_pat_keys: - interp["defaults"] = "" - - if key in pats: - interp["defaults"] = pats[key] - - pats[key] = config.get(s_pats, key, vars=interp) - - if s_defs in config: - defs.update(config.items(s_defs, vars=interp)) - - # fetch inputs and outputs - if "inputs" not in pats: - raise ZakeConfigError( - "Required Zake field 'inputs' missing from section Paths " - f"while reading target {tname}." - ) - - for inp in pats["inputs"].strip().split(): - targ.add_input((inp, "")) - - if "injects" in pats: - for injs in pats["injects"].strip().split(): - match = self.inj_field_pat.match(injs) - - if not match: - raise ZakeConfigError( - "Malformed value found in given Zake field 'injects': " - + injs - ) - - inp, out = match.groups() - targ.add_input((inp, out)) - - for bundle in bundles: - out_name = "output." + bundle - matchers_name = "matchers." + bundle - excluders_name = "excluders." + bundle - excluders_global_name = "excluders" - priority_name = "priority." + bundle - - priority = config.getfloat( - s_bundle_cfg, - priority_name, - vars=interp, - fallback=config.getfloat( - "Partition", priority_name, vars=interp, fallback=None - ), - ) - - if priority is not None: - targ.outputs[bundle].priority = priority - - if out_name in pats: - targ.outputs[bundle].output = pats[out_name].strip() - - else: - raise ZakeConfigError( - f"Required Zake field '{out_name}' missing from section Paths " - f"while reading target {tname}." - ) - - matchers = get_bundle_cfg(s_bundle_cfg, matchers_name) - excluders = get_bundle_cfg(s_bundle_cfg, excluders_name) - excluders_global = get_bundle_cfg(s_bundle_cfg, excluders_global_name) - - if matchers: - for matcher in matchers.strip().split(): - targ.outputs[bundle].add_matcher(matcher) - - if excluders: - for excluder in excluders.strip().split(): - targ.outputs[bundle].add_excluder(excluder) - - if excluders_global: - for excluder in excluders_global.strip().split(): - targ.outputs[bundle].add_excluder(excluder) - - # preprocessor definitions - for name, value in defs.items(): - targ.add_definition(name.strip(), value.strip()) + self.process_targets(targs, bundles, tname, interp, config, get_bundle_cfg) # return parsed targets return targs @@ -278,3 +316,7 @@ def main(print_status_code=True): if __name__ == "__main__": main() + + +if __name__ == "__main__": + main()