flaremc 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flaremc-0.2.0 → flaremc-0.2.2}/PKG-INFO +1 -1
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/__init__.py +2 -2
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/cli.py +49 -9
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/command_parser.py +18 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/compiler.py +3 -18
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/context.py +107 -26
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/control_flow.py +56 -83
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/execute_modifiers.py +34 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/preprocessor.py +153 -55
- flaremc-0.2.2/flare/validator/__init__.py +1 -0
- flaremc-0.2.2/flare/validator/core.py +89 -0
- flaremc-0.2.2/flare/validator/matchers.py +160 -0
- flaremc-0.2.2/flare/validator/parser.py +68 -0
- flaremc-0.2.2/flare/validator/schema.py +29 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/core.py +29 -5
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/nbt.py +195 -98
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/score.py +26 -21
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/selector.py +13 -25
- {flaremc-0.2.0 → flaremc-0.2.2}/flaremc.egg-info/PKG-INFO +1 -1
- {flaremc-0.2.0 → flaremc-0.2.2}/flaremc.egg-info/SOURCES.txt +5 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/pyproject.toml +1 -1
- {flaremc-0.2.0 → flaremc-0.2.2}/LICENSE +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/README.md +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/__main__.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/math.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/nbt_schema.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/types.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/__init__.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/bigscore.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/complex.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/float32.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/float64.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flare/variables/storage.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flaremc.egg-info/dependency_links.txt +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flaremc.egg-info/entry_points.txt +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flaremc.egg-info/requires.txt +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flaremc.egg-info/top_level.txt +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/flarevsc/node_modules/flatted/python/flatted.py +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/setup.cfg +0 -0
- {flaremc-0.2.0 → flaremc-0.2.2}/tests/test_flare.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .compiler import _flatten_and, _eval_to_bool_score, _compile_relational
|
|
2
2
|
from .context import namespace, export, tick, push_context, runcommand, files, temp_obj, constant_obj, vars_obj, \
|
|
3
|
-
constants, _flare_assign, _flare_print, dbg, _flare_return
|
|
4
|
-
from .control_flow import _flare_if, _flare_while, _flare_for, _flare_with
|
|
3
|
+
constants, _flare_assign, _flare_aug_assign, _flare_print, dbg, _flare_return
|
|
4
|
+
from .control_flow import _flare_if, _flare_while, _flare_for, _flare_with, _flare_break, _flare_continue
|
|
5
5
|
from .execute_modifiers import _as, at, positioned, aligned, facing, anchored, rotated, dimension, applyon, on, summon, \
|
|
6
6
|
store, ExecuteChain, StoreExecuteChain
|
|
7
7
|
from .math import round_, floor, ceil
|
|
@@ -16,6 +16,8 @@ from watchdog.observers import Observer
|
|
|
16
16
|
from . import context
|
|
17
17
|
from .preprocessor import FlareTransformer, CallGraphAnalyzer, preprocess_minecraft_commands
|
|
18
18
|
|
|
19
|
+
build_lock = threading.RLock()
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
def init_project(path: str):
|
|
21
23
|
p = Path(path)
|
|
@@ -42,7 +44,7 @@ def init_project(path: str):
|
|
|
42
44
|
print(f"Created {json_path.absolute()}")
|
|
43
45
|
|
|
44
46
|
|
|
45
|
-
def
|
|
47
|
+
def _build_datapack_inner(file_path: str, cli_overrides: dict = None):
|
|
46
48
|
p = Path(file_path).parent
|
|
47
49
|
json_path = p / "flare.json"
|
|
48
50
|
|
|
@@ -50,13 +52,21 @@ def build_datapack(file_path: str):
|
|
|
50
52
|
with open(json_path, "r") as f:
|
|
51
53
|
config = json.load(f)
|
|
52
54
|
else:
|
|
53
|
-
config = {"namespace": "flare", "pack_format": 15, "description": "A Flare datapack", "build_dir": "dist"
|
|
55
|
+
config = {"namespace": "flare", "pack_format": 15, "description": "A Flare datapack", "build_dir": "dist",
|
|
56
|
+
"validation_level": "strict", "minecraft_version": "1.20.4"}
|
|
57
|
+
|
|
58
|
+
if cli_overrides:
|
|
59
|
+
config.update(cli_overrides)
|
|
54
60
|
|
|
55
61
|
namespace = config.get("namespace", "flare")
|
|
56
62
|
build_dir = Path(config.get("build_dir", "dist"))
|
|
57
63
|
if not build_dir.is_absolute():
|
|
58
64
|
build_dir = p / build_dir
|
|
59
65
|
|
|
66
|
+
context.validation_level = config.get("validation_level", "strict")
|
|
67
|
+
context.minecraft_version = config.get("minecraft_version", "1.20.4")
|
|
68
|
+
context.nbt_schema_missing = config.get("nbt_schema_missing", "error")
|
|
69
|
+
|
|
60
70
|
context.reset_context()
|
|
61
71
|
context._current_namespace = namespace
|
|
62
72
|
|
|
@@ -85,11 +95,14 @@ def build_datapack(file_path: str):
|
|
|
85
95
|
|
|
86
96
|
global_env = {"__name__": "__main__", "__file__": abs_path}
|
|
87
97
|
exec(
|
|
88
|
-
"from flare import _flare_assign, _flare_if, _flare_while, _flare_for, _flare_with, runcommand, _flare_return\n"
|
|
98
|
+
"from flare import _flare_assign, _flare_aug_assign, _flare_if, _flare_while, _flare_for, _flare_with, runcommand, _flare_return, _flare_break, _flare_continue\n"
|
|
89
99
|
"from flare.command_parser import interpolate_command\n"
|
|
90
100
|
"from flare import _flare_print as print, selector, _as, at, positioned, aligned, facing, anchored, rotated, dimension, applyon, on, summon, store\n"
|
|
91
|
-
"from flare import nbt, score, fixed,
|
|
92
|
-
|
|
101
|
+
"from flare import nbt, score, fixed, tagged, ref, getscore, storage, array, byte, boolean, short, long, double\n"
|
|
102
|
+
"from flare import nbtbyte, nbtbool, nbtshort, nbtint, nbtlong, nbtfloat, nbtdouble, nbtstr, nbtlist, nbtdict, nbtbytearray, nbtintarray, nbtlongarray\n"
|
|
103
|
+
"from flare import round_, floor, ceil\n"
|
|
104
|
+
"from flare.math import *\n"
|
|
105
|
+
"from flare import dbg, export, namespace, tick", global_env)
|
|
93
106
|
|
|
94
107
|
exec(compile(tree, abs_path, "exec"), global_env)
|
|
95
108
|
sys.path.pop(0)
|
|
@@ -100,6 +113,9 @@ def build_datapack(file_path: str):
|
|
|
100
113
|
|
|
101
114
|
new_modules = set(sys.modules.keys()) - old_modules
|
|
102
115
|
watch_files = {os.path.abspath(file_path)}
|
|
116
|
+
if os.path.exists(p / "flare.json"):
|
|
117
|
+
watch_files.add(os.path.abspath(p / "flare.json"))
|
|
118
|
+
|
|
103
119
|
for mod_name in new_modules:
|
|
104
120
|
mod = sys.modules.get(mod_name)
|
|
105
121
|
if mod and getattr(mod, "__file__", None):
|
|
@@ -157,6 +173,11 @@ def build_datapack(file_path: str):
|
|
|
157
173
|
return True, watch_files, build_dir
|
|
158
174
|
|
|
159
175
|
|
|
176
|
+
def build_datapack(file_path: str, cli_overrides: dict = None):
|
|
177
|
+
with build_lock:
|
|
178
|
+
return _build_datapack_inner(file_path, cli_overrides)
|
|
179
|
+
|
|
180
|
+
|
|
160
181
|
class WatcherHandler(FileSystemEventHandler):
|
|
161
182
|
def __init__(self, cli_args, watch_files):
|
|
162
183
|
self.cli_args = cli_args
|
|
@@ -195,8 +216,27 @@ def main():
|
|
|
195
216
|
parser.add_argument("--watch", action="store_true", help="Watch for file changes and rebuild")
|
|
196
217
|
parser.add_argument("--run", nargs="?", const="-1", default=None,
|
|
197
218
|
help="Run the compiled datapack in mcemu. Optionally specify a timeout in seconds.")
|
|
198
|
-
|
|
199
|
-
|
|
219
|
+
parser.add_argument("--nbt-schema-missing", choices=["error", "warning", "ignore"], default="error",
|
|
220
|
+
help="Action when indexing an NBT path that does not exist in the attached schema.")
|
|
221
|
+
|
|
222
|
+
args, unknown_args = parser.parse_known_args()
|
|
223
|
+
|
|
224
|
+
cli_overrides = {}
|
|
225
|
+
if hasattr(args, 'nbt_schema_missing'):
|
|
226
|
+
cli_overrides["nbt_schema_missing"] = args.nbt_schema_missing
|
|
227
|
+
i = 0
|
|
228
|
+
while i < len(unknown_args):
|
|
229
|
+
arg = unknown_args[i]
|
|
230
|
+
if arg.startswith("--"):
|
|
231
|
+
key = arg[2:].replace("-", "_")
|
|
232
|
+
if i + 1 < len(unknown_args) and not unknown_args[i + 1].startswith("--"):
|
|
233
|
+
cli_overrides[key] = unknown_args[i + 1]
|
|
234
|
+
i += 2
|
|
235
|
+
else:
|
|
236
|
+
cli_overrides[key] = True
|
|
237
|
+
i += 1
|
|
238
|
+
else:
|
|
239
|
+
i += 1
|
|
200
240
|
|
|
201
241
|
is_init = args.target == "init"
|
|
202
242
|
if is_init:
|
|
@@ -220,7 +260,7 @@ def main():
|
|
|
220
260
|
print(f"Error: Target file {file_path} not found.")
|
|
221
261
|
return
|
|
222
262
|
|
|
223
|
-
success, watch_files, build_dir = build_datapack(file_path)
|
|
263
|
+
success, watch_files, build_dir = build_datapack(file_path, cli_overrides)
|
|
224
264
|
|
|
225
265
|
emu_thread = None
|
|
226
266
|
running = True
|
|
@@ -271,7 +311,7 @@ def main():
|
|
|
271
311
|
running = False
|
|
272
312
|
emu_thread.join()
|
|
273
313
|
|
|
274
|
-
success, new_watch_files, build_dir = build_datapack(file_path)
|
|
314
|
+
success, new_watch_files, build_dir = build_datapack(file_path, cli_overrides)
|
|
275
315
|
|
|
276
316
|
if success and new_watch_files != watch_files:
|
|
277
317
|
observer.unschedule_all()
|
|
@@ -74,6 +74,15 @@ def interpolate_command(command: str, local_vars: dict, global_vars: dict) -> st
|
|
|
74
74
|
output.append(val.addr)
|
|
75
75
|
elif hasattr(val, "target"):
|
|
76
76
|
output.append(val.target)
|
|
77
|
+
elif isinstance(val, dict) and output and output[-1].endswith("**"):
|
|
78
|
+
output[-1] = output[-1][:-2]
|
|
79
|
+
items = []
|
|
80
|
+
for k, v in val.items():
|
|
81
|
+
if isinstance(k, str) and not re.match(r'^[a-zA-Z0-9_\-\.]+$', k):
|
|
82
|
+
k = json.dumps(k)
|
|
83
|
+
v_str = json.dumps(v) if isinstance(v, (str, dict, list)) else str(v)
|
|
84
|
+
items.append(f"{k}: {v_str}")
|
|
85
|
+
output.append(", ".join(items))
|
|
77
86
|
else:
|
|
78
87
|
output.append(str(val))
|
|
79
88
|
elif not is_key and ident in global_vars:
|
|
@@ -82,6 +91,15 @@ def interpolate_command(command: str, local_vars: dict, global_vars: dict) -> st
|
|
|
82
91
|
output.append(val.addr)
|
|
83
92
|
elif hasattr(val, "target"):
|
|
84
93
|
output.append(val.target)
|
|
94
|
+
elif isinstance(val, dict) and output and output[-1].endswith("**"):
|
|
95
|
+
output[-1] = output[-1][:-2]
|
|
96
|
+
items = []
|
|
97
|
+
for k, v in val.items():
|
|
98
|
+
if isinstance(k, str) and not re.match(r'^[a-zA-Z0-9_\-\.]+$', k):
|
|
99
|
+
k = json.dumps(k)
|
|
100
|
+
v_str = json.dumps(v) if isinstance(v, (str, dict, list)) else str(v)
|
|
101
|
+
items.append(f"{k}: {v_str}")
|
|
102
|
+
output.append(", ".join(items))
|
|
85
103
|
else:
|
|
86
104
|
output.append(str(val))
|
|
87
105
|
else:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from . import context as ctx
|
|
2
2
|
from .context import runcommand, temp_obj
|
|
3
|
-
from .
|
|
4
|
-
from .variables import score, nbt, BinaryOp, UnaryOp, getscore
|
|
3
|
+
from .variables import score, BinaryOp, UnaryOp, getscore
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def _compile_relational(node, invert=False):
|
|
@@ -80,22 +79,8 @@ def _eval_to_bool_score(node):
|
|
|
80
79
|
|
|
81
80
|
|
|
82
81
|
def _flatten_and(node, invert=False):
|
|
83
|
-
if
|
|
84
|
-
|
|
85
|
-
node = BinaryOp(node.length(), 0, "ne")
|
|
86
|
-
else:
|
|
87
|
-
node = BinaryOp(node, 0, "ne")
|
|
88
|
-
if isinstance(node, UnaryOp) and node.op == "neg":
|
|
89
|
-
node = BinaryOp(node, 0, "ne")
|
|
90
|
-
if isinstance(node, UnaryOp) and node.op == "not":
|
|
91
|
-
return _flatten_and(node.operand, not invert)
|
|
92
|
-
if isinstance(node, BinaryOp):
|
|
93
|
-
if node.op == "and" and not invert:
|
|
94
|
-
return _flatten_and(node.left, invert) + _flatten_and(node.right, invert)
|
|
95
|
-
if node.op == "or" and invert:
|
|
96
|
-
return _flatten_and(node.left, invert) + _flatten_and(node.right, invert)
|
|
97
|
-
if node.op in ("eq", "ne", "lt", "le", "gt", "ge"):
|
|
98
|
-
return [_compile_relational(node, invert)]
|
|
82
|
+
if hasattr(node, "__branch__"):
|
|
83
|
+
return node.__branch__(invert)
|
|
99
84
|
|
|
100
85
|
dest = _eval_to_bool_score(node)
|
|
101
86
|
keyword = "unless" if invert else "if"
|
|
@@ -18,11 +18,16 @@ _constant_offset = 0
|
|
|
18
18
|
_recursive_functions = set()
|
|
19
19
|
_in_recursive_context = False
|
|
20
20
|
return_types = {}
|
|
21
|
+
has_returns = {}
|
|
21
22
|
_logical_func = None
|
|
22
23
|
|
|
24
|
+
validation_level = "strict"
|
|
25
|
+
minecraft_version = "1.20.4"
|
|
26
|
+
nbt_schema_missing = "error"
|
|
27
|
+
|
|
23
28
|
|
|
24
29
|
def reset_context():
|
|
25
|
-
global files, current_file, _current_namespace, functions, constants, _temp_id, _func_id, _objective_offset, _constant_offset
|
|
30
|
+
global files, current_file, _current_namespace, functions, constants, _temp_id, _func_id, _objective_offset, _constant_offset, validation_level, minecraft_version, nbt_schema_missing, _recursive_functions, _in_recursive_context, return_types, _logical_func
|
|
26
31
|
files = {"main": []}
|
|
27
32
|
current_file = "main"
|
|
28
33
|
_current_namespace = "flare"
|
|
@@ -91,13 +96,22 @@ def namespace(name: str):
|
|
|
91
96
|
_current_namespace = name
|
|
92
97
|
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
return len(str(x).split(".")[-1])
|
|
99
|
+
from .validator import validate_command, FlareCommandValidationError
|
|
96
100
|
|
|
97
101
|
|
|
98
102
|
def runcommand(command: str, local_vars=None, global_vars=None):
|
|
99
103
|
if local_vars is not None and global_vars is not None:
|
|
100
104
|
command = interpolate_command(command, local_vars, global_vars)
|
|
105
|
+
|
|
106
|
+
if validation_level != "none":
|
|
107
|
+
try:
|
|
108
|
+
validate_command(command, minecraft_version)
|
|
109
|
+
except FlareCommandValidationError as e:
|
|
110
|
+
if validation_level == "strict":
|
|
111
|
+
raise e
|
|
112
|
+
elif validation_level == "warning":
|
|
113
|
+
print(f"[Flare Compiler Warning] {e}")
|
|
114
|
+
|
|
101
115
|
files[current_file].append(command)
|
|
102
116
|
|
|
103
117
|
|
|
@@ -117,11 +131,13 @@ def _flare_print(*args):
|
|
|
117
131
|
if i > 0:
|
|
118
132
|
components.append({"text": " "})
|
|
119
133
|
|
|
120
|
-
if hasattr(arg,
|
|
121
|
-
|
|
134
|
+
if hasattr(arg, "__icopy__") and getattr(type(arg), "__name__", "") in ("BinaryOp", "UnaryOp"):
|
|
135
|
+
global _temp_id
|
|
136
|
+
arg = arg.__icopy__(f"!print_{_temp_id}")
|
|
137
|
+
_temp_id += 1
|
|
122
138
|
|
|
123
139
|
if isinstance(arg, score):
|
|
124
|
-
if getattr(arg,
|
|
140
|
+
if getattr(arg, "multiplier", 1.0) != 1.0:
|
|
125
141
|
scale_str = f"{arg.multiplier:.15f}".rstrip("0")
|
|
126
142
|
if scale_str.endswith("."):
|
|
127
143
|
scale_str += "0"
|
|
@@ -182,15 +198,15 @@ def export(func=None, *, append=False):
|
|
|
182
198
|
for name, param in sig.parameters.items():
|
|
183
199
|
anno = param.annotation
|
|
184
200
|
|
|
185
|
-
if hasattr(anno,
|
|
201
|
+
if hasattr(anno, "__name__") and anno.__name__ in ("score", "fixed", "_PrecisionScore"):
|
|
186
202
|
if is_recursive:
|
|
187
203
|
raise TypeError(
|
|
188
204
|
f"Recursive function '{func.__name__}' argument '{name}' needs a stack but it's a score.")
|
|
189
|
-
if anno.__name__ ==
|
|
205
|
+
if anno.__name__ == "_PrecisionScore" or anno.__name__ == "fixed":
|
|
190
206
|
kwargs[name] = anno(addr=f"{func.__name__}_{name} {vars_obj}")
|
|
191
207
|
else:
|
|
192
208
|
kwargs[name] = score(addr=f"{func.__name__}_{name} {vars_obj}")
|
|
193
|
-
elif hasattr(anno,
|
|
209
|
+
elif hasattr(anno, "__name__") and anno.__name__ in ("nbt", "_TypedNBT"):
|
|
194
210
|
if is_recursive:
|
|
195
211
|
kwargs[name] = anno(addr=f"storage flare:args {func.__name__}_{name}[-1]")
|
|
196
212
|
else:
|
|
@@ -203,7 +219,8 @@ def export(func=None, *, append=False):
|
|
|
203
219
|
if sig.return_annotation is not inspect.Signature.empty:
|
|
204
220
|
return_types[func_name] = sig.return_annotation
|
|
205
221
|
else:
|
|
206
|
-
return_types[func_name] =
|
|
222
|
+
return_types[func_name] = "UNKNOWN"
|
|
223
|
+
has_returns[func_name] = False
|
|
207
224
|
|
|
208
225
|
global _in_recursive_context, _logical_func
|
|
209
226
|
prev_recursive = _in_recursive_context
|
|
@@ -247,26 +264,28 @@ def export(func=None, *, append=False):
|
|
|
247
264
|
base_addr = f"storage flare:args {func.__name__}_{arg_name}"
|
|
248
265
|
runcommand(f"data remove {base_addr}[-1]")
|
|
249
266
|
|
|
250
|
-
ret_anno = sig.return_annotation
|
|
251
|
-
if ret_anno
|
|
252
|
-
|
|
267
|
+
ret_anno = return_types.get(func_name, sig.return_annotation)
|
|
268
|
+
if ret_anno == "UNKNOWN":
|
|
269
|
+
return "UNKNOWN_RETURN"
|
|
270
|
+
elif ret_anno is not inspect.Signature.empty and ret_anno is not None:
|
|
271
|
+
if hasattr(ret_anno, "__name__") and ret_anno.__name__ in ("score", "fixed", "_PrecisionScore"):
|
|
253
272
|
temp_ret = score(addr=f"!ret{_temp_id} {temp_obj}")
|
|
254
273
|
_temp_id += 1
|
|
255
274
|
runcommand(
|
|
256
|
-
f"scoreboard players operation {temp_ret.addr} = {func_name.replace(
|
|
257
|
-
if ret_anno.__name__ in (
|
|
275
|
+
f"scoreboard players operation {temp_ret.addr} = {func_name.replace(":", "_")}_ret {vars_obj}")
|
|
276
|
+
if ret_anno.__name__ in ("fixed", "_PrecisionScore"):
|
|
258
277
|
pass
|
|
259
278
|
return temp_ret
|
|
260
279
|
else:
|
|
261
280
|
temp_ret = nbt(addr=f"storage flare:temp !ret{_temp_id}")
|
|
262
281
|
_temp_id += 1
|
|
263
282
|
runcommand(
|
|
264
|
-
f"data modify {temp_ret.addr} set from storage flare:returns {func_name.replace(
|
|
283
|
+
f"data modify {temp_ret.addr} set from storage flare:returns {func_name.replace(":", "_")}")
|
|
265
284
|
return temp_ret
|
|
266
285
|
|
|
267
286
|
proxy = ProxyFunction()
|
|
268
287
|
|
|
269
|
-
func_globals = getattr(func,
|
|
288
|
+
func_globals = getattr(func, "__globals__", {})
|
|
270
289
|
prev_func = func_globals.get(func.__name__)
|
|
271
290
|
func_globals[func.__name__] = proxy
|
|
272
291
|
|
|
@@ -281,6 +300,13 @@ def export(func=None, *, append=False):
|
|
|
281
300
|
else:
|
|
282
301
|
func_globals.pop(func.__name__, None)
|
|
283
302
|
|
|
303
|
+
if return_types[func_name] == "UNKNOWN":
|
|
304
|
+
if has_returns.get(func_name, False):
|
|
305
|
+
raise TypeError(
|
|
306
|
+
f"Function {func_name} has returns but return type could not be auto-detected. Please add an explicit return type annotation.")
|
|
307
|
+
else:
|
|
308
|
+
return_types[func_name] = None
|
|
309
|
+
|
|
284
310
|
return proxy
|
|
285
311
|
|
|
286
312
|
|
|
@@ -293,8 +319,11 @@ def _flare_assign(var_name, value, local_env, global_env):
|
|
|
293
319
|
target = None
|
|
294
320
|
|
|
295
321
|
if target is not None and hasattr(target, "__iset__"):
|
|
296
|
-
|
|
297
|
-
|
|
322
|
+
try:
|
|
323
|
+
target.__iset__(value)
|
|
324
|
+
return target
|
|
325
|
+
except Exception:
|
|
326
|
+
pass
|
|
298
327
|
|
|
299
328
|
if target is None and hasattr(value, "__icopy__"):
|
|
300
329
|
if "is_recursive" in inspect.signature(value.__icopy__).parameters:
|
|
@@ -304,6 +333,35 @@ def _flare_assign(var_name, value, local_env, global_env):
|
|
|
304
333
|
return value
|
|
305
334
|
|
|
306
335
|
|
|
336
|
+
def _flare_aug_assign(var_name, op_name, value, _locals, _globals):
|
|
337
|
+
if var_name in _locals:
|
|
338
|
+
var = _locals[var_name]
|
|
339
|
+
elif var_name in _globals:
|
|
340
|
+
var = _globals[var_name]
|
|
341
|
+
else:
|
|
342
|
+
raise NameError(f"name '{var_name}' is not defined")
|
|
343
|
+
|
|
344
|
+
op_map = {"Add": "__iadd__", "Sub": "__isub__", "Mult": "__imul__", "Div": "__itruediv__", "Mod": "__imod__"}
|
|
345
|
+
method_name = op_map.get(op_name)
|
|
346
|
+
|
|
347
|
+
if hasattr(var, method_name):
|
|
348
|
+
getattr(var, method_name)(value)
|
|
349
|
+
else:
|
|
350
|
+
if op_name == "Add":
|
|
351
|
+
var += value
|
|
352
|
+
elif op_name == "Sub":
|
|
353
|
+
var -= value
|
|
354
|
+
elif op_name == "Mult":
|
|
355
|
+
var *= value
|
|
356
|
+
elif op_name == "Div":
|
|
357
|
+
var /= value
|
|
358
|
+
elif op_name == "Mod":
|
|
359
|
+
var %= value
|
|
360
|
+
_flare_assign(var_name, var, _locals, _globals)
|
|
361
|
+
|
|
362
|
+
return value
|
|
363
|
+
|
|
364
|
+
|
|
307
365
|
def _flare_return(value):
|
|
308
366
|
from .variables import score, nbt # avoid circular import
|
|
309
367
|
func_name = _logical_func
|
|
@@ -311,22 +369,45 @@ def _flare_return(value):
|
|
|
311
369
|
raise Exception("Return outside of exported function")
|
|
312
370
|
ret_anno = return_types.get(func_name, None)
|
|
313
371
|
|
|
314
|
-
if
|
|
372
|
+
if isinstance(value, str) and value == "UNKNOWN_RETURN":
|
|
373
|
+
has_returns[func_name] = True
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
ret_anno = return_types.get(func_name, None)
|
|
377
|
+
|
|
378
|
+
if ret_anno == "UNKNOWN":
|
|
379
|
+
if hasattr(value, "_best_leaf"):
|
|
380
|
+
leaf = value._best_leaf()
|
|
381
|
+
else:
|
|
382
|
+
leaf = value
|
|
383
|
+
|
|
384
|
+
if leaf is None:
|
|
385
|
+
ret_anno = type(None)
|
|
386
|
+
elif hasattr(type(leaf), "__name__") and type(leaf).__name__ in ("score", "fixed", "_PrecisionScore", "nbt",
|
|
387
|
+
"_TypedNBT"):
|
|
388
|
+
ret_anno = type(leaf)
|
|
389
|
+
else:
|
|
390
|
+
raise TypeError(f"Cannot auto-detect return type from value of type {type(leaf)}")
|
|
391
|
+
return_types[func_name] = ret_anno
|
|
392
|
+
|
|
393
|
+
has_returns[func_name] = True
|
|
394
|
+
|
|
395
|
+
if ret_anno is None or ret_anno == type(None):
|
|
315
396
|
if value is not None:
|
|
316
397
|
raise TypeError(f"Function {func_name} returned a value but has no return type annotation")
|
|
317
398
|
return
|
|
318
399
|
|
|
319
|
-
if hasattr(ret_anno,
|
|
320
|
-
target = score(addr=f"{func_name.replace(
|
|
400
|
+
if hasattr(ret_anno, "__name__") and ret_anno.__name__ in ("score", "fixed", "_PrecisionScore"):
|
|
401
|
+
target = score(addr=f"{func_name.replace(":", "_")}_ret {vars_obj}")
|
|
321
402
|
target.__iset__(value)
|
|
322
403
|
else:
|
|
323
404
|
if inspect.isclass(ret_anno) and issubclass(ret_anno, nbt):
|
|
324
|
-
target = ret_anno(addr=f"storage flare:returns {func_name.replace(
|
|
405
|
+
target = ret_anno(addr=f"storage flare:returns {func_name.replace(":", "_")}")
|
|
325
406
|
else:
|
|
326
407
|
datatype = None
|
|
327
|
-
if hasattr(ret_anno,
|
|
328
|
-
datatype = getattr(ret_anno,
|
|
329
|
-
target = nbt(addr=f"storage flare:returns {func_name.replace(
|
|
408
|
+
if hasattr(ret_anno, "__origin__") or isinstance(ret_anno, type):
|
|
409
|
+
datatype = getattr(ret_anno, "__origin__", ret_anno)
|
|
410
|
+
target = nbt(addr=f"storage flare:returns {func_name.replace(":", "_")}", datatype=datatype)
|
|
330
411
|
target.__iset__(value)
|
|
331
412
|
|
|
332
413
|
runcommand("return 1")
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
from . import context as ctx
|
|
2
|
-
from .execute_modifiers import ExecuteChain, _as
|
|
3
2
|
from .compiler import _flatten_and
|
|
4
3
|
from .context import push_context, runcommand, temp_obj
|
|
5
|
-
from .
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
from .variables import score
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _flare_break():
|
|
8
|
+
runcommand(f"scoreboard players set !break {temp_obj} 1")
|
|
9
|
+
runcommand("return 0")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _flare_continue():
|
|
13
|
+
runcommand("return 0")
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
def _flare_if(*args):
|
|
@@ -107,12 +113,27 @@ def _flare_if(*args):
|
|
|
107
113
|
runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
|
|
108
114
|
|
|
109
115
|
|
|
110
|
-
def _flare_while(cond_func, body_func):
|
|
116
|
+
def _flare_while(cond_func, body_func, orelse_func=None, has_break=False, has_continue=False):
|
|
111
117
|
func_name = f"{ctx._current_namespace}:while_{ctx._func_id}"
|
|
112
118
|
ctx._func_id += 1
|
|
113
119
|
|
|
114
120
|
with push_context(func_name):
|
|
115
|
-
|
|
121
|
+
if has_break or has_continue:
|
|
122
|
+
func_body = f"{ctx._current_namespace}:while_body_{ctx._func_id}"
|
|
123
|
+
ctx._func_id += 1
|
|
124
|
+
with push_context(func_body):
|
|
125
|
+
body_func()
|
|
126
|
+
|
|
127
|
+
ret_body = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
|
|
128
|
+
ctx._temp_id += 1
|
|
129
|
+
runcommand(f"execute store result score {ret_body.addr} run function {func_body}")
|
|
130
|
+
runcommand(f"execute if score {ret_body.addr} matches 1 run return 1")
|
|
131
|
+
|
|
132
|
+
if has_break:
|
|
133
|
+
runcommand(f"execute if score !break {temp_obj} matches 1 run return 0")
|
|
134
|
+
else:
|
|
135
|
+
body_func()
|
|
136
|
+
|
|
116
137
|
cond = cond_func()
|
|
117
138
|
conds = _flatten_and(cond)
|
|
118
139
|
prefix = f"execute {' '.join(conds)}"
|
|
@@ -122,6 +143,9 @@ def _flare_while(cond_func, body_func):
|
|
|
122
143
|
runcommand(f"execute store result score {ret_temp.addr} {' '.join(conds)} run function {func_name}")
|
|
123
144
|
runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
|
|
124
145
|
|
|
146
|
+
if has_break:
|
|
147
|
+
runcommand(f"scoreboard players set !break {temp_obj} 0")
|
|
148
|
+
|
|
125
149
|
cond_init = cond_func()
|
|
126
150
|
conds_init = _flatten_and(cond_init)
|
|
127
151
|
prefix_init = f"execute {' '.join(conds_init)}"
|
|
@@ -130,91 +154,40 @@ def _flare_while(cond_func, body_func):
|
|
|
130
154
|
runcommand(f"execute store result score {ret_temp_init.addr} {' '.join(conds_init)} run function {func_name}")
|
|
131
155
|
runcommand(f"execute if score {ret_temp_init.addr} matches 1 run return 1")
|
|
132
156
|
|
|
157
|
+
if orelse_func:
|
|
158
|
+
if has_break:
|
|
159
|
+
orelse_name = f"{ctx._current_namespace}:while_else_{ctx._func_id}"
|
|
160
|
+
ctx._func_id += 1
|
|
161
|
+
with push_context(orelse_name):
|
|
162
|
+
orelse_func()
|
|
163
|
+
runcommand(f"execute if score !break {temp_obj} matches 0 run function {orelse_name}")
|
|
164
|
+
else:
|
|
165
|
+
orelse_func()
|
|
133
166
|
|
|
134
|
-
def _flare_for(iterable, body_func):
|
|
135
|
-
if isinstance(iterable, nbt) and iterable.is_sequence():
|
|
136
|
-
elem_type = None
|
|
137
|
-
if iterable.type == NBTType.ByteArray:
|
|
138
|
-
elem_type = NBTType.Byte
|
|
139
|
-
elif iterable.type == NBTType.IntArray:
|
|
140
|
-
elem_type = NBTType.Int
|
|
141
|
-
elif iterable.type == NBTType.LongArray:
|
|
142
|
-
elem_type = NBTType.Long
|
|
143
|
-
|
|
144
|
-
temp_arr = nbt(addr=f"flare:temp !for_arr_{ctx._temp_id}", datatype=iterable.type)
|
|
145
|
-
temp_var = nbt(addr=f"{temp_arr.addr}[0]", datatype=elem_type)
|
|
146
|
-
ctx._temp_id += 1
|
|
147
|
-
|
|
148
|
-
temp_arr.__iset__(iterable)
|
|
149
|
-
length_score = temp_arr.length()
|
|
150
|
-
|
|
151
|
-
func_name = f"{ctx._current_namespace}:for_{ctx._func_id}"
|
|
152
|
-
ctx._func_id += 1
|
|
153
|
-
|
|
154
|
-
with push_context(func_name):
|
|
155
|
-
body_func(temp_var)
|
|
156
|
-
runcommand(f"data remove {temp_arr.addr}[0]")
|
|
157
|
-
|
|
158
|
-
length_score -= 1
|
|
159
|
-
ctx.files[func_name].append("return 0")
|
|
160
|
-
ret_temp = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
|
|
161
|
-
ctx._temp_id += 1
|
|
162
|
-
runcommand(
|
|
163
|
-
f"execute store result score {ret_temp.addr} if score {length_score.addr} matches 1.. run function {func_name}")
|
|
164
|
-
runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
|
|
165
|
-
|
|
166
|
-
ret_temp_init = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
|
|
167
|
-
ctx._temp_id += 1
|
|
168
|
-
runcommand(
|
|
169
|
-
f"execute store result score {ret_temp_init.addr} if score {length_score.addr} matches 1.. run function {func_name}")
|
|
170
|
-
runcommand(f"execute if score {ret_temp_init.addr} matches 1 run return 1")
|
|
171
167
|
|
|
168
|
+
def _flare_for(iterable, body_func, orelse_func=None, has_break=False, has_continue=False):
|
|
169
|
+
if hasattr(iterable, "__for__"):
|
|
170
|
+
iterable.__for__(body_func, orelse_func, has_break, has_continue)
|
|
172
171
|
else:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
else:
|
|
176
|
-
for item in iterable:
|
|
177
|
-
body_func(item)
|
|
172
|
+
for item in iterable:
|
|
173
|
+
body_func(item)
|
|
178
174
|
|
|
179
175
|
|
|
180
176
|
def _flare_with(*args):
|
|
181
177
|
body_func = args[-1]
|
|
182
178
|
chains = args[:-1]
|
|
183
179
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
chain = _as(chain)
|
|
188
|
-
if isinstance(chain, ExecuteChain):
|
|
189
|
-
if chain.fragments and chain.fragments[0] == "execute":
|
|
190
|
-
combined_fragments.extend(chain.fragments[1:])
|
|
191
|
-
else:
|
|
192
|
-
combined_fragments.extend(chain.fragments)
|
|
193
|
-
elif isinstance(chain, str):
|
|
194
|
-
combined_fragments.append(chain)
|
|
195
|
-
|
|
196
|
-
prefix = " ".join(combined_fragments)
|
|
197
|
-
|
|
198
|
-
func_name = f"{ctx._current_namespace}:with_{ctx._func_id}"
|
|
199
|
-
ctx._func_id += 1
|
|
200
|
-
|
|
201
|
-
with push_context(func_name):
|
|
202
|
-
body_func()
|
|
203
|
-
|
|
204
|
-
if ctx.files.get(func_name):
|
|
205
|
-
if len(ctx.files[func_name]) == 1:
|
|
206
|
-
cmd = ctx.files[func_name][0]
|
|
207
|
-
del ctx.files[func_name]
|
|
208
|
-
if cmd.startswith("execute "):
|
|
209
|
-
runcommand(f"{prefix} {cmd[8:]}")
|
|
210
|
-
else:
|
|
211
|
-
runcommand(f"{prefix} run {cmd}")
|
|
180
|
+
def wrap(idx):
|
|
181
|
+
if idx == len(chains):
|
|
182
|
+
body_func()
|
|
212
183
|
else:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
184
|
+
obj = chains[idx]
|
|
185
|
+
if hasattr(obj, "__with__"):
|
|
186
|
+
obj.__with__(lambda: wrap(idx + 1))
|
|
187
|
+
elif isinstance(obj, str):
|
|
188
|
+
from .execute_modifiers import ExecuteChain
|
|
189
|
+
ExecuteChain(obj).__with__(lambda: wrap(idx + 1))
|
|
218
190
|
else:
|
|
219
|
-
|
|
220
|
-
|
|
191
|
+
raise TypeError(f"Object of type {type(obj).__name__} does not support __with__")
|
|
192
|
+
|
|
193
|
+
wrap(0)
|