diff --git a/mcdp/command.py b/mcdp/command.py index ed7ce1e..37f4323 100644 --- a/mcdp/command.py +++ b/mcdp/command.py @@ -7,7 +7,7 @@ from .config import get_config from .typings import McdpBaseModel, McdpVar, McdpError from .mcstring import MCString -from .context import Context, comment, insert, ContextEnv, newline +from .context import Context, McdpContextError, comment, insert, ContextEnv, newline from .exceptions import * @@ -196,25 +196,28 @@ def __str__(self) -> str: class ContextInstructionEnv(ContextEnv): - __slots__ = ["instruction"] + __slots__ = ["instruction", "decorate"] - def __init__(self, instruction: "Instruction") -> None: + def __init__(self, instruction: "Instruction", *, decorate: bool = True) -> None: self.instruction = instruction + self.decorate = decorate super().__init__(instruction.__class__.__name__) def init(self) -> None: super().init() - comment(f"{repr(self.instruction)} file.") + comment(f"{repr(self.instruction.__class__.__name__)} file.") newline(2) def decorate_command(self, cmd: str) -> str: + if not self.decorate: + return cmd exec = Execute(self.instruction) return exec.command_prefix() + cmd class Instruction(McdpBaseModel): - __slots__ = ["stream"] + stream: Optional[Context] = None def __init__(self) -> None: raise NotImplementedError @@ -231,19 +234,21 @@ def __exit__(self, exc_type, exc_ins, traceback) -> None: Context.pop_env() async def __aenter__(self) -> "Instruction": - env = ContextInstructionEnv(self) + env = ContextInstructionEnv(self, decorate=False) self.stream = env.creat_stream() await self.stream.__aenter__() return self async def __aexit__(self, exc_type, exc_ins, traceback) -> None: + if not self.stream: + raise McdpContextError("No context provide.") await self.stream.__aexit__(exc_type, exc_ins, traceback) def __str__(self) -> str: raise NotImplementedError def __repr__(self) -> str: - return f"Instruction({self})" + return f"{self.__class__.__name__}({self})" class AlignInstruction(Instruction): @@ -696,6 +701,22 @@ def __str__(self) -> str: def case(type: str, *args, **kwds) -> _Case: return _case_register[type](*args, **kwds) + +class ContextLibEnv(ContextEnv): + + __slots__ = ["func"] + + def __init__(self, func: Callable[[], Optional[Coroutine]]) -> None: + self.func = func + super().__init__("Library") + + def init(self) -> None: + comment( + f"Library function {self.func.__name__} of Mcdp.", + ) + newline() + + class Function(McdpVar): """ This class is only used to create library. @@ -716,7 +737,7 @@ def __call__(self, namespace: Optional[str] = None) -> None: namespace = namespace or get_config().namespace name = self.func.__name__ - if not self.space: + if not self.space or self.space == '.': insert(f"function {namespace}:{name}") else: insert(f"function {namespace}:{self.space}/{name}") @@ -724,7 +745,7 @@ def __call__(self, namespace: Optional[str] = None) -> None: async def apply(self) -> None: if self.space: Context.enter_space(self.space) - async with Context(self.func.__name__): + async with Context(self.func.__name__, envs=[ContextLibEnv(self.func)]): if iscoroutinefunction(self.func): await self.func() else: diff --git a/mcdp/context.py b/mcdp/context.py index 6f1fe3a..cb1ca1a 100644 --- a/mcdp/context.py +++ b/mcdp/context.py @@ -120,8 +120,8 @@ class ContextMeta(type): stack: StackCache environments: list - enter: EnvMethod - leave: EnvMethod + enter: staticmethod + leave: staticmethod def init(self, path: T_Path) -> None: self.path = Path(path, "functions").resolve() @@ -137,16 +137,16 @@ async def __aenter__(self) -> "ContextMeta": """ Init the datapack. """ - default_env = self("__init__", root_path=self.path) + default_env = self("__init__", root_path=self.path, envs=ContextEnv("__init__")) await self.stack.append(default_env) - comment( - "This is the initize function." - ) + comment("This is the initize function.") newline(2) + + self.enter() + insert("tag @e[tag=Mcdp_stack] add Mcdp_home") TagManager("functions", namespace="minecraft") TagManager("functions", namespace=get_namespace()) insert(f"function {get_namespace()}:__init_score__") - self.enter() return self async def __aexit__(self, exc_type, exc_ins, traceback) -> None: diff --git a/mcdp/mcfunc.py b/mcdp/mcfunc.py index 59688c3..58b54e8 100644 --- a/mcdp/mcfunc.py +++ b/mcdp/mcfunc.py @@ -1,3 +1,4 @@ +import time import warnings from asyncio import iscoroutinefunction, run from inspect import signature, Parameter @@ -8,8 +9,8 @@ from .file_struct import build_dirs_from_config from .typings import McdpVar, Variable from .config import get_config, MCFuncConfig -from .context import Context, TagManager, add_tag, insert, comment, newline, leave_stack_ops, enter_stack_ops -from .command import Function, Selector, lib_func +from .context import Context, ContextEnv, TagManager, add_tag, insert, comment, newline, leave_stack_ops, enter_stack_ops +from .command import AsInstruction, Function, Selector, lib_func from .entities import McdpStack, get_tag from .variable import Score, Scoreboard, dp_int, dp_score, global_var, init_global from .exceptions import * @@ -37,13 +38,25 @@ def _get_arguments(name: str, param: Mapping[str, Parameter]) -> Tuple[list, dic return args, kwds -def add_comment(func: Callable) -> None: - sig = signature(func) - comment(f"Function '{func.__name__}'", f"\nSignature:\n{sig}") - if func.__doc__: - comment("\nDoc:", func.__doc__) - newline(2) +class ContextFunctionEnv(ContextEnv): + __slots__ = ["func"] + + def __init__(self, func: Callable) -> None: + self.func = func + super().__init__("mcfunction") + + def init(self) -> None: + sig = signature(self.func) + + comment(f"Function '{self.func.__name__}'") + newline() + comment(f"Signature:\n{sig}") + if self.func.__doc__: + newline() + comment("Doc:", self.func.__doc__) + newline(2) + class MCFunction(Function): @@ -64,7 +77,7 @@ def __init__(self, name: str, *, namespace: Optional[str] = None, config: MCFunc self.__name__ = name self.namespace = namespace self.config = config - self.overload: List = [] + self.overload: List[Callable] = [] self.overload_counter: List[Counter] = [] self.__class__.collection[name] = self @@ -84,6 +97,8 @@ def register(self, func: Callable) -> None: if not func.__name__ == self.__name__: warnings.warn( f"unsuit function name. Maybe you are deliberate?", RuntimeWarning) + if self.overload: + func.__name__ += str(len(self.overload)) self.overload.append(func) if len(self.overload) > 1 and not self.config.allow_overload: @@ -98,25 +113,19 @@ async def apply(self) -> None: f = self.overload[i] if not (len(self.overload) == 1 or self.overload_counter[i]): - print(Context.get_relative_path()) continue - - if i == 0: - name = self.__name__ - else: - name = self.__name__ + str(i) - async with Context(name): + async with Context(f.__name__, envs=ContextFunctionEnv(f)): for t in self.config.tag: add_tag(t) - if get_config().pydp.add_comments: - add_comment(f) - sig = signature(f).parameters args, kwds = _get_arguments(self.__name__, sig) - ans = f(*args, **kwds) + if iscoroutinefunction(f): + ans = await f(*args, **kwds) + else: + ans = f(*args, **kwds) if isinstance(ans, Score): if ans.name != "dpc_return": dp_score("dpc_return", ans, stack_id=-2, @@ -141,7 +150,6 @@ def __call__(self, *args: Any, **kwds: Any) -> Any: except: continue else: - ind = i +self.overload_counter[i] break else: @@ -152,15 +160,12 @@ def __call__(self, *args: Any, **kwds: Any) -> Any: dp_score("mcfarg_{0}_{1}".format(self.__name__, k), v, display={"text": f"Mcdp function {self.__name__} arguments", "color": "dark_blue"}) - path = Context.get_relative_path() / self.__name__ - Context.enter() - self.namespace = self.namespace or get_config().namespace - file = f"{self.namespace}:{path}" - if ind != 0: - insert(f"execute as @e[tag=stack_top] run function {file}{ind}") - else: - insert(f"execute as @e[tag=stack_top] run function {file}") + self.space = str(Context.get_relative_path()) + self.func = self.overload[i] + Context.enter() + with AsInstruction(Selector("@e", "tag=stack_top", tag=get_tag())): + super().__call__() T_ret = sig.return_annotation if not T_ret is None: return dp_score("dpc_return", init=False, simulation=T_ret, @@ -199,25 +204,38 @@ def mcfunc_main(*flags: str, **kw: Any) -> Callable[[Callable], None]: def mcfunc_main(func: Optional[Union[Callable, str]] = None, *flags, **kw): async def mf_main(func: Callable[[], Any], cfg: MCFuncConfig) -> None: #config = get_config() + start = time.process_time_ns() await build_dirs_from_config() async with Context: add_tag("minecraft:load") init_global() - async with Context("__main__"): + async with Context("__main__", envs=ContextEnv("__main__")): + comment("This is the main function of the datapack.") + newline(2) + for t in cfg.tag: add_tag(t) + Context.enter() if iscoroutinefunction(func): await func() else: func() + Context.leave() + + async with Context('__init_score__', envs=ContextEnv("__init_score__")): + comment("Init the scoreborad.") + newline(2) - async with Context('__init_score__'): Scoreboard.apply_all() await MCFunction.apply_all() await TagManager.apply_all() - + + end = time.process_time_ns() + process = end - start + process *= 1e-8 + print(f"Complite completed in {process} ms") get_counter().print_out() cfg = MCFuncConfig(**kw) @@ -275,9 +293,6 @@ def enter_stack() -> None: top.remove_tag("stack_top") stack = McdpStack() - - with mcdp_stack_id == 0: - stack.add_tag("Mcdp_home") mcdp_stack_id += 1