Skip to content

Commit 4edc8af

Browse files
committed
Allow recursion in requires functions
1 parent 002debd commit 4edc8af

File tree

1 file changed

+26
-16
lines changed

1 file changed

+26
-16
lines changed

src/Rules.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,27 +78,37 @@ def checkRequireStringForArea(state: CollectionState, area: dict):
7878
if requires_list == "":
7979
return True
8080

81-
for item in re.findall(r'\{(\w+)\((.*?)\)\}', requires_list):
82-
func_name = item[0]
83-
func_args = item[1].split(",")
84-
if func_args == ['']:
85-
func_args.pop()
81+
def findAndRecursivelyExecuteFunctions(requires_list: str, maxRecursion: int, recursionDepth: int = 0) -> str:
82+
found_functions = re.findall(r'\{(\w+)\((.*?)\)\}', requires_list)
83+
if found_functions:
84+
if recursionDepth >= maxRecursion:
85+
raise RecursionError(f'the require in {area.get("name", f"An area with these parameters: {area}")} looped too many time ({maxRecursion})')
86+
else:
87+
for item in found_functions:
88+
func_name = item[0]
89+
func_args = item[1].split(",")
90+
if func_args == ['']:
91+
func_args.pop()
8692

87-
func = globals().get(func_name)
93+
func = globals().get(func_name)
8894

89-
if func is None:
90-
func = getattr(Rules, func_name, None)
95+
if func is None:
96+
func = getattr(Rules, func_name, None)
9197

92-
if not callable(func):
93-
raise ValueError(f"Invalid function `{func_name}` in {area}.")
98+
if not callable(func):
99+
raise ValueError(f"Invalid function `{func_name}` in {area}.")
94100

95-
convert_req_function_args(func, func_args, area.get("name", f"An area with these parameters: {area}"))
96-
result = func(world, multiworld, state, player, *func_args)
97-
if isinstance(result, bool):
98-
requires_list = requires_list.replace("{" + func_name + "(" + item[1] + ")}", "1" if result else "0")
99-
else:
100-
requires_list = requires_list.replace("{" + func_name + "(" + item[1] + ")}", str(result))
101+
convert_req_function_args(func, func_args, area.get("name", f"An area with these parameters: {area}"))
102+
result = func(world, multiworld, state, player, *func_args)
103+
if isinstance(result, bool):
104+
requires_list = requires_list.replace("{" + func_name + "(" + item[1] + ")}", "1" if result else "0")
105+
else:
106+
requires_list = requires_list.replace("{" + func_name + "(" + item[1] + ")}", str(result))
107+
108+
requires_list = findAndRecursivelyExecuteFunctions(requires_list, maxRecursion, recursionDepth + 1)
109+
return requires_list
101110

111+
requires_list = findAndRecursivelyExecuteFunctions(requires_list, maxRecursion=5)
102112

103113
# parse user written statement into list of each item
104114
for item in re.findall(r'\|[^|]+\|', requires_list):

0 commit comments

Comments
 (0)