flaremc 0.2.2__tar.gz → 0.2.3__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.
Files changed (40) hide show
  1. {flaremc-0.2.2 → flaremc-0.2.3}/PKG-INFO +1 -1
  2. {flaremc-0.2.2 → flaremc-0.2.3}/flare/cli.py +7 -0
  3. {flaremc-0.2.2 → flaremc-0.2.3}/flare/command_parser.py +2 -2
  4. {flaremc-0.2.2 → flaremc-0.2.3}/flare/compiler.py +12 -12
  5. {flaremc-0.2.2 → flaremc-0.2.3}/flare/context.py +68 -32
  6. {flaremc-0.2.2 → flaremc-0.2.3}/flare/control_flow.py +41 -39
  7. {flaremc-0.2.2 → flaremc-0.2.3}/flare/execute_modifiers.py +7 -7
  8. {flaremc-0.2.2 → flaremc-0.2.3}/flare/preprocessor.py +52 -7
  9. {flaremc-0.2.2 → flaremc-0.2.3}/flare/validator/matchers.py +35 -2
  10. {flaremc-0.2.2 → flaremc-0.2.3}/flare/validator/schema.py +9 -1
  11. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/bigscore.py +40 -40
  12. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/core.py +2 -2
  13. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/float32.py +119 -119
  14. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/float64.py +41 -41
  15. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/nbt.py +190 -169
  16. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/score.py +148 -148
  17. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/selector.py +7 -7
  18. {flaremc-0.2.2 → flaremc-0.2.3}/flaremc.egg-info/PKG-INFO +1 -1
  19. {flaremc-0.2.2 → flaremc-0.2.3}/pyproject.toml +1 -1
  20. {flaremc-0.2.2 → flaremc-0.2.3}/LICENSE +0 -0
  21. {flaremc-0.2.2 → flaremc-0.2.3}/README.md +0 -0
  22. {flaremc-0.2.2 → flaremc-0.2.3}/flare/__init__.py +0 -0
  23. {flaremc-0.2.2 → flaremc-0.2.3}/flare/__main__.py +0 -0
  24. {flaremc-0.2.2 → flaremc-0.2.3}/flare/math.py +0 -0
  25. {flaremc-0.2.2 → flaremc-0.2.3}/flare/nbt_schema.py +0 -0
  26. {flaremc-0.2.2 → flaremc-0.2.3}/flare/types.py +0 -0
  27. {flaremc-0.2.2 → flaremc-0.2.3}/flare/validator/__init__.py +0 -0
  28. {flaremc-0.2.2 → flaremc-0.2.3}/flare/validator/core.py +0 -0
  29. {flaremc-0.2.2 → flaremc-0.2.3}/flare/validator/parser.py +0 -0
  30. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/__init__.py +0 -0
  31. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/complex.py +0 -0
  32. {flaremc-0.2.2 → flaremc-0.2.3}/flare/variables/storage.py +0 -0
  33. {flaremc-0.2.2 → flaremc-0.2.3}/flaremc.egg-info/SOURCES.txt +0 -0
  34. {flaremc-0.2.2 → flaremc-0.2.3}/flaremc.egg-info/dependency_links.txt +0 -0
  35. {flaremc-0.2.2 → flaremc-0.2.3}/flaremc.egg-info/entry_points.txt +0 -0
  36. {flaremc-0.2.2 → flaremc-0.2.3}/flaremc.egg-info/requires.txt +0 -0
  37. {flaremc-0.2.2 → flaremc-0.2.3}/flaremc.egg-info/top_level.txt +0 -0
  38. {flaremc-0.2.2 → flaremc-0.2.3}/flarevsc/node_modules/flatted/python/flatted.py +0 -0
  39. {flaremc-0.2.2 → flaremc-0.2.3}/setup.cfg +0 -0
  40. {flaremc-0.2.2 → flaremc-0.2.3}/tests/test_flare.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flaremc
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Flare datapack compiler framework
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -96,6 +96,7 @@ def _build_datapack_inner(file_path: str, cli_overrides: dict = None):
96
96
  global_env = {"__name__": "__main__", "__file__": abs_path}
97
97
  exec(
98
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"
99
+ "from flare import context as ctx\n"
99
100
  "from flare.command_parser import interpolate_command\n"
100
101
  "from flare import _flare_print as print, selector, _as, at, positioned, aligned, facing, anchored, rotated, dimension, applyon, on, summon, store\n"
101
102
  "from flare import nbt, score, fixed, tagged, ref, getscore, storage, array, byte, boolean, short, long, double\n"
@@ -153,8 +154,14 @@ def _build_datapack_inner(file_path: str, cli_overrides: dict = None):
153
154
  if ":" in filename:
154
155
  ns, name = filename.split(":", 1)
155
156
  file_p = build_dir / "data" / ns / "functions" / f"{name}.mcfunction"
157
+ is_top_level = "generated_" not in name and "while_" not in name and name not in ("main", "load")
156
158
  else:
157
159
  file_p = build_dir / "data" / context._current_namespace / "functions" / f"{filename}.mcfunction"
160
+ is_top_level = "generated_" not in filename and "while_" not in filename and filename not in ("main",
161
+ "load")
162
+
163
+ if is_top_level and lines and lines[-1] in ("return 1", "return 0"):
164
+ lines.pop()
158
165
 
159
166
  file_p.parent.mkdir(parents=True, exist_ok=True)
160
167
  with open(file_p, "w") as f:
@@ -71,7 +71,7 @@ def interpolate_command(command: str, local_vars: dict, global_vars: dict) -> st
71
71
  elif not is_key and ident in local_vars:
72
72
  val = local_vars[ident]
73
73
  if hasattr(val, "addr"):
74
- output.append(val.addr)
74
+ output.append(val._addr)
75
75
  elif hasattr(val, "target"):
76
76
  output.append(val.target)
77
77
  elif isinstance(val, dict) and output and output[-1].endswith("**"):
@@ -88,7 +88,7 @@ def interpolate_command(command: str, local_vars: dict, global_vars: dict) -> st
88
88
  elif not is_key and ident in global_vars:
89
89
  val = global_vars[ident]
90
90
  if hasattr(val, "addr"):
91
- output.append(val.addr)
91
+ output.append(val._addr)
92
92
  elif hasattr(val, "target"):
93
93
  output.append(val.target)
94
94
  elif isinstance(val, dict) and output and output[-1].endswith("**"):
@@ -29,9 +29,9 @@ def _compile_relational(node, invert=False):
29
29
 
30
30
  if not isinstance(right, score):
31
31
  if isinstance(right, (int, float)):
32
- right = getscore(right, left.multiplier)
32
+ right = getscore(right, left._multiplier)
33
33
  else:
34
- t = score(addr=f"!c{ctx._temp_id} {temp_obj}", multiplier=left.multiplier)
34
+ t = score(addr=f"!c{ctx._temp_id} {temp_obj}", multiplier=left._multiplier)
35
35
  ctx._temp_id += 1
36
36
  if isinstance(right, (BinaryOp, UnaryOp)):
37
37
  right._eval_into(t)
@@ -39,42 +39,42 @@ def _compile_relational(node, invert=False):
39
39
  t.__iset__(right)
40
40
  right = t
41
41
 
42
- if left.multiplier != right.multiplier:
43
- t = score(addr=f"!c{ctx._temp_id} {temp_obj}", multiplier=left.multiplier)
42
+ if left._multiplier != right._multiplier:
43
+ t = score(addr=f"!c{ctx._temp_id} {temp_obj}", multiplier=left._multiplier)
44
44
  ctx._temp_id += 1
45
45
  t.__iset__(right)
46
46
  right = t
47
47
 
48
- return f"{keyword} score {left.addr} {mcop} {right.addr}"
48
+ return f"{keyword} score {left._addr} {mcop} {right._addr}"
49
49
 
50
50
 
51
51
  def _eval_to_bool_score(node):
52
52
  dest = score(addr=f"!b{ctx._temp_id} {temp_obj}")
53
53
  ctx._temp_id += 1
54
- runcommand(f"scoreboard players set {dest.addr} 0")
54
+ runcommand(f"scoreboard players set {dest._addr} 0")
55
55
 
56
56
  if isinstance(node, BinaryOp) and node.op == "or":
57
57
  left_conds = _flatten_and(node.left)
58
- runcommand(f"execute {' '.join(left_conds)} run scoreboard players set {dest.addr} 1")
58
+ runcommand(f"execute {' '.join(left_conds)} run scoreboard players set {dest._addr} 1")
59
59
  right_conds = _flatten_and(node.right)
60
60
  runcommand(
61
- f"execute if score {dest.addr} matches 0 {' '.join(right_conds)} run scoreboard players set {dest.addr} 1")
61
+ f"execute if score {dest._addr} matches 0 {' '.join(right_conds)} run scoreboard players set {dest._addr} 1")
62
62
  return dest
63
63
 
64
64
  if isinstance(node, UnaryOp) and node.op == "not":
65
65
  sub_dest = _eval_to_bool_score(node.operand)
66
- runcommand(f"execute if score {sub_dest.addr} matches 0 run scoreboard players set {dest.addr} 1")
66
+ runcommand(f"execute if score {sub_dest._addr} matches 0 run scoreboard players set {dest._addr} 1")
67
67
  return dest
68
68
 
69
69
  if isinstance(node, (BinaryOp, UnaryOp)):
70
70
  t = score(addr=f"!b{ctx._temp_id} {temp_obj}")
71
71
  ctx._temp_id += 1
72
72
  node._eval_into(t)
73
- runcommand(f"execute unless score {t.addr} matches 0 run scoreboard players set {dest.addr} 1")
73
+ runcommand(f"execute unless score {t._addr} matches 0 run scoreboard players set {dest._addr} 1")
74
74
  return dest
75
75
 
76
76
  if node:
77
- runcommand(f"scoreboard players set {dest.addr} 1")
77
+ runcommand(f"scoreboard players set {dest._addr} 1")
78
78
  return dest
79
79
 
80
80
 
@@ -84,4 +84,4 @@ def _flatten_and(node, invert=False):
84
84
 
85
85
  dest = _eval_to_bool_score(node)
86
86
  keyword = "unless" if invert else "if"
87
- return [f"{keyword} score {dest.addr} matches 1"]
87
+ return [f"{keyword} score {dest._addr} matches 1"]
@@ -8,9 +8,39 @@ current_file = "main"
8
8
  _current_namespace = "flare"
9
9
  functions = {}
10
10
  constants = {}
11
- constant_obj = "__flare__constant__"
12
- vars_obj = "__flare__vars__"
13
- temp_obj = "__flare__temp__"
11
+
12
+
13
+ class DynamicVar:
14
+ def __init__(self, fmt):
15
+ self.fmt = fmt
16
+
17
+ def __str__(self):
18
+ return self.fmt.format(ns=_current_namespace)
19
+
20
+ def __format__(self, format_spec):
21
+ return format(str(self), format_spec)
22
+
23
+ def __add__(self, other):
24
+ return str(self) + other
25
+
26
+ def __radd__(self, other):
27
+ return other + str(self)
28
+
29
+ def __hash__(self):
30
+ return hash(str(self))
31
+
32
+ def __eq__(self, other):
33
+ return str(self) == str(other)
34
+
35
+
36
+ constant_obj = DynamicVar("__{ns}__constant__")
37
+ vars_obj = DynamicVar("__{ns}__vars__")
38
+ temp_obj = DynamicVar("__{ns}__temp__")
39
+ temp_storage = DynamicVar("{ns}:__flare_temp__")
40
+ vars_storage = DynamicVar("{ns}:__flare_vars__")
41
+ args_storage = DynamicVar("{ns}:__flare_args__")
42
+ returns_storage = DynamicVar("{ns}:__flare_returns__")
43
+
14
44
  _temp_id = 0
15
45
  _func_id = 0
16
46
  _objective_offset = 0
@@ -138,28 +168,28 @@ def _flare_print(*args):
138
168
 
139
169
  if isinstance(arg, score):
140
170
  if getattr(arg, "multiplier", 1.0) != 1.0:
141
- scale_str = f"{arg.multiplier:.15f}".rstrip("0")
171
+ scale_str = f"{arg._multiplier:.15f}".rstrip("0")
142
172
  if scale_str.endswith("."):
143
173
  scale_str += "0"
144
174
  runcommand(
145
- f"execute store result storage flare:temp __flare_debug_{i} double {scale_str} run scoreboard players get {arg.addr}")
146
- components.append({"nbt": f"__flare_debug_{i}", "storage": "flare:temp"})
175
+ f"execute store result storage {temp_storage} __flare_debug_{i} double {scale_str} run scoreboard players get {arg._addr}")
176
+ components.append({"nbt": f"__flare_debug_{i}", "storage": str(temp_storage)})
147
177
  else:
148
- name, obj = arg.addr.split(" ", 1)
178
+ name, obj = arg._addr.split(" ", 1)
149
179
  components.append({"score": {"name": name, "objective": obj}})
150
180
  elif isinstance(arg, nbt):
151
- nbt_comp = {"nbt": arg.path or "{}"}
152
- if arg.path == "":
181
+ nbt_comp = {"nbt": arg._path or "{}"}
182
+ if arg._path == "":
153
183
  nbt_comp["nbt"] = "{}"
154
184
 
155
- if arg.target_type == "storage":
156
- nbt_comp["storage"] = arg.target
157
- elif arg.target_type == "entity":
158
- nbt_comp["entity"] = arg.target
159
- elif arg.target_type == "block":
160
- nbt_comp["block"] = arg.target
185
+ if arg._target_type == "storage":
186
+ nbt_comp["storage"] = arg._target
187
+ elif arg._target_type == "entity":
188
+ nbt_comp["entity"] = arg._target
189
+ elif arg._target_type == "block":
190
+ nbt_comp["block"] = arg._target
161
191
 
162
- if arg.path == "":
192
+ if arg._path == "":
163
193
  nbt_comp["nbt"] = "{}"
164
194
 
165
195
  components.append(nbt_comp)
@@ -178,15 +208,21 @@ def _flare_print(*args):
178
208
  runcommand(f"tellraw @a {cmd_text}")
179
209
 
180
210
 
181
- def export(func=None, *, append=False):
211
+ def export(func=None, *, name=None, append=False):
182
212
  from flare import score # avoid circular import
213
+
214
+ if isinstance(func, str):
215
+ name = func
216
+ func = None
217
+
183
218
  if func is None:
184
219
  def wrapper(f):
185
- return export(f, append=append)
220
+ return export(f, name=name, append=append)
186
221
 
187
222
  return wrapper
188
223
 
189
- func_name = f"{_current_namespace}:{func.__name__}"
224
+ actual_name = name if name is not None else func.__name__
225
+ func_name = f"{_current_namespace}:{actual_name}"
190
226
  if func_name in files and not append:
191
227
  raise ValueError(f"Function {func_name} already exists. Use @export(append=True) to append.")
192
228
 
@@ -208,9 +244,9 @@ def export(func=None, *, append=False):
208
244
  kwargs[name] = score(addr=f"{func.__name__}_{name} {vars_obj}")
209
245
  elif hasattr(anno, "__name__") and anno.__name__ in ("nbt", "_TypedNBT"):
210
246
  if is_recursive:
211
- kwargs[name] = anno(addr=f"storage flare:args {func.__name__}_{name}[-1]")
247
+ kwargs[name] = anno(addr=f"storage {args_storage} {func.__name__}_{name}[-1]")
212
248
  else:
213
- kwargs[name] = anno(addr=f"storage flare:args {func.__name__}_{name}")
249
+ kwargs[name] = anno(addr=f"storage {args_storage} {func.__name__}_{name}")
214
250
  elif anno is not inspect.Signature.empty:
215
251
  raise TypeError(f"Argument '{name}' must be typed as score or nbt, not {anno}")
216
252
  else:
@@ -239,20 +275,20 @@ def export(func=None, *, append=False):
239
275
  target = kwargs[arg_name]
240
276
 
241
277
  if is_recursive and isinstance(target, nbt):
242
- base_addr = f"storage flare:args {func.__name__}_{arg_name}"
278
+ base_addr = f"storage {args_storage} {func.__name__}_{arg_name}"
243
279
  if isinstance(arg_val, (int, float, str)):
244
280
  runcommand(f"data modify {base_addr} append value {json.dumps(arg_val)}")
245
281
  elif isinstance(arg_val, nbt):
246
- runcommand(f"data modify {base_addr} append from {arg_val.addr}")
282
+ runcommand(f"data modify {base_addr} append from {arg_val._addr}")
247
283
  elif isinstance(arg_val, score):
248
284
  runcommand(f"data modify {base_addr} append value 0")
249
285
  runcommand(
250
- f"execute store result {base_addr}[-1] int {1 / arg_val.multiplier} run scoreboard players get {arg_val.addr}")
286
+ f"execute store result {base_addr}[-1] int {1 / arg_val._multiplier} run scoreboard players get {arg_val._addr}")
251
287
  elif hasattr(type(arg_val), "_eval_into"):
252
- temp = nbt(addr=f"storage flare:temp !t{_temp_id}", datatype=target.type)
288
+ temp = nbt(addr=f"storage {temp_storage} !t{_temp_id}", datatype=target._type)
253
289
  _temp_id += 1
254
290
  arg_val._eval_into(temp)
255
- runcommand(f"data modify {base_addr} append from {temp.addr}")
291
+ runcommand(f"data modify {base_addr} append from {temp._addr}")
256
292
  else:
257
293
  target.__iset__(arg_val)
258
294
 
@@ -261,7 +297,7 @@ def export(func=None, *, append=False):
261
297
  if is_recursive:
262
298
  for arg_name in bound.arguments.keys():
263
299
  if isinstance(kwargs[arg_name], nbt):
264
- base_addr = f"storage flare:args {func.__name__}_{arg_name}"
300
+ base_addr = f"storage {args_storage} {func.__name__}_{arg_name}"
265
301
  runcommand(f"data remove {base_addr}[-1]")
266
302
 
267
303
  ret_anno = return_types.get(func_name, sig.return_annotation)
@@ -272,15 +308,15 @@ def export(func=None, *, append=False):
272
308
  temp_ret = score(addr=f"!ret{_temp_id} {temp_obj}")
273
309
  _temp_id += 1
274
310
  runcommand(
275
- f"scoreboard players operation {temp_ret.addr} = {func_name.replace(":", "_")}_ret {vars_obj}")
311
+ f"scoreboard players operation {temp_ret._addr} = {func_name.replace(":", "_")}_ret {vars_obj}")
276
312
  if ret_anno.__name__ in ("fixed", "_PrecisionScore"):
277
313
  pass
278
314
  return temp_ret
279
315
  else:
280
- temp_ret = nbt(addr=f"storage flare:temp !ret{_temp_id}")
316
+ temp_ret = nbt(addr=f"storage {temp_storage} !ret{_temp_id}")
281
317
  _temp_id += 1
282
318
  runcommand(
283
- f"data modify {temp_ret.addr} set from storage flare:returns {func_name.replace(":", "_")}")
319
+ f"data modify {temp_ret._addr} set from storage {returns_storage} {func_name.replace(':', '_')}")
284
320
  return temp_ret
285
321
 
286
322
  proxy = ProxyFunction()
@@ -402,12 +438,12 @@ def _flare_return(value):
402
438
  target.__iset__(value)
403
439
  else:
404
440
  if inspect.isclass(ret_anno) and issubclass(ret_anno, nbt):
405
- target = ret_anno(addr=f"storage flare:returns {func_name.replace(":", "_")}")
441
+ target = ret_anno(addr=f"storage {returns_storage} {func_name.replace(':', '_')}")
406
442
  else:
407
443
  datatype = None
408
444
  if hasattr(ret_anno, "__origin__") or isinstance(ret_anno, type):
409
445
  datatype = getattr(ret_anno, "__origin__", ret_anno)
410
- target = nbt(addr=f"storage flare:returns {func_name.replace(":", "_")}", datatype=datatype)
446
+ target = nbt(addr=f"storage {returns_storage} {func_name.replace(':', '_')}", datatype=datatype)
411
447
  target.__iset__(value)
412
448
 
413
449
  runcommand("return 1")
@@ -1,9 +1,36 @@
1
1
  from . import context as ctx
2
2
  from .compiler import _flatten_and
3
3
  from .context import push_context, runcommand, temp_obj
4
+ from .execute_modifiers import ExecuteChain
4
5
  from .variables import score
5
6
 
6
7
 
8
+ def _has_early_return(func_name):
9
+ for cmd in ctx.files.get(func_name, []):
10
+ if cmd == "return 1" or cmd.endswith(" run return 1"):
11
+ return True
12
+ return False
13
+
14
+
15
+ def _invoke_block(func_name, cond_str):
16
+ if _has_early_return(func_name):
17
+ if not (ctx.files[func_name] and ctx.files[func_name][-1] in ("return 0", "return 1")):
18
+ ctx.files[func_name].append("return 0")
19
+ ret_temp = score(addr=f"!ret{ctx._temp_id} {ctx.temp_obj}")
20
+ ctx._temp_id += 1
21
+
22
+ if cond_str:
23
+ runcommand(f"execute store result score {ret_temp._addr} {cond_str} run function {func_name}")
24
+ else:
25
+ runcommand(f"execute store result score {ret_temp._addr} run function {func_name}")
26
+ runcommand(f"execute if score {ret_temp._addr} matches 1 run return 1")
27
+ else:
28
+ if cond_str:
29
+ runcommand(f"execute {cond_str} run function {func_name}")
30
+ else:
31
+ runcommand(f"function {func_name}")
32
+
33
+
7
34
  def _flare_break():
8
35
  runcommand(f"scoreboard players set !break {temp_obj} 1")
9
36
  runcommand("return 0")
@@ -24,7 +51,7 @@ def _flare_if(*args):
24
51
  if has_else_or_elif:
25
52
  sometemp = score(addr=f"!elif{ctx._temp_id} {temp_obj}")
26
53
  ctx._temp_id += 1
27
- runcommand(f"scoreboard players set {sometemp.addr} 0")
54
+ runcommand(f"scoreboard players set {sometemp._addr} 0")
28
55
 
29
56
  for cond_func, body_func in zip(conditions, bodies):
30
57
  if cond_func is None:
@@ -38,16 +65,11 @@ def _flare_if(*args):
38
65
  cmd = ctx.files[func_name][0]
39
66
  del ctx.files[func_name]
40
67
  if cmd.startswith("execute "):
41
- runcommand(f"execute if score {sometemp.addr} matches 0 {cmd[8:]}")
68
+ runcommand(f"execute if score {sometemp._addr} matches 0 {cmd[8:]}")
42
69
  else:
43
- runcommand(f"execute if score {sometemp.addr} matches 0 run {cmd}")
70
+ runcommand(f"execute if score {sometemp._addr} matches 0 run {cmd}")
44
71
  else:
45
- ctx.files[func_name].append("return 0")
46
- ret_temp = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
47
- ctx._temp_id += 1
48
- runcommand(
49
- f"execute store result score {ret_temp.addr} if score {sometemp.addr} matches 0 run function {func_name}")
50
- runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
72
+ _invoke_block(func_name, f"if score {sometemp._addr} matches 0")
51
73
  else:
52
74
  body_func()
53
75
  break
@@ -67,16 +89,11 @@ def _flare_if(*args):
67
89
  cmd = ctx.files[func_name][0]
68
90
  del ctx.files[func_name]
69
91
  if cmd.startswith("execute "):
70
- runcommand(f"execute if score {sometemp.addr} matches 0 {cmd[8:]}")
92
+ runcommand(f"execute if score {sometemp._addr} matches 0 {cmd[8:]}")
71
93
  else:
72
- runcommand(f"execute if score {sometemp.addr} matches 0 run {cmd}")
94
+ runcommand(f"execute if score {sometemp._addr} matches 0 run {cmd}")
73
95
  else:
74
- ctx.files[func_name].append("return 0")
75
- ret_temp = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
76
- ctx._temp_id += 1
77
- runcommand(
78
- f"execute store result score {ret_temp.addr} if score {sometemp.addr} matches 0 run function {func_name}")
79
- runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
96
+ _invoke_block(func_name, f"if score {sometemp._addr} matches 0")
80
97
  else:
81
98
  body_func()
82
99
  break
@@ -85,13 +102,13 @@ def _flare_if(*args):
85
102
  prefix = f"execute {' '.join(conds)}"
86
103
 
87
104
  if sometemp is not None:
88
- prefix = f"execute if score {sometemp.addr} matches 0 {' '.join(conds)}"
105
+ prefix = f"execute if score {sometemp._addr} matches 0 {' '.join(conds)}"
89
106
 
90
107
  func_name = f"{ctx._current_namespace}:generated_{ctx._func_id}"
91
108
  ctx._func_id += 1
92
109
  with push_context(func_name):
93
110
  if sometemp is not None:
94
- runcommand(f"scoreboard players set {sometemp.addr} 1")
111
+ runcommand(f"scoreboard players set {sometemp._addr} 1")
95
112
  body_func()
96
113
 
97
114
  if ctx.files.get(func_name):
@@ -103,14 +120,7 @@ def _flare_if(*args):
103
120
  else:
104
121
  runcommand(f"{prefix} run {cmd}")
105
122
  else:
106
- ctx.files[func_name].append("return 0")
107
- ret_temp = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
108
- ctx._temp_id += 1
109
- if prefix.startswith("execute "):
110
- runcommand(f"execute store result score {ret_temp.addr} {prefix[8:]} run function {func_name}")
111
- else:
112
- runcommand(f"execute store result score {ret_temp.addr} run function {func_name}")
113
- runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
123
+ _invoke_block(func_name, prefix[8:] if prefix.startswith("execute ") else "")
114
124
 
115
125
 
116
126
  def _flare_while(cond_func, body_func, orelse_func=None, has_break=False, has_continue=False):
@@ -124,10 +134,7 @@ def _flare_while(cond_func, body_func, orelse_func=None, has_break=False, has_co
124
134
  with push_context(func_body):
125
135
  body_func()
126
136
 
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")
137
+ _invoke_block(func_body, "")
131
138
 
132
139
  if has_break:
133
140
  runcommand(f"execute if score !break {temp_obj} matches 1 run return 0")
@@ -137,11 +144,7 @@ def _flare_while(cond_func, body_func, orelse_func=None, has_break=False, has_co
137
144
  cond = cond_func()
138
145
  conds = _flatten_and(cond)
139
146
  prefix = f"execute {' '.join(conds)}"
140
- ctx.files[func_name].append("return 0")
141
- ret_temp = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
142
- ctx._temp_id += 1
143
- runcommand(f"execute store result score {ret_temp.addr} {' '.join(conds)} run function {func_name}")
144
- runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
147
+ _invoke_block(func_name, " ".join(conds))
145
148
 
146
149
  if has_break:
147
150
  runcommand(f"scoreboard players set !break {temp_obj} 0")
@@ -151,8 +154,8 @@ def _flare_while(cond_func, body_func, orelse_func=None, has_break=False, has_co
151
154
  prefix_init = f"execute {' '.join(conds_init)}"
152
155
  ret_temp_init = score(addr=f"!ret{ctx._temp_id} {temp_obj}")
153
156
  ctx._temp_id += 1
154
- runcommand(f"execute store result score {ret_temp_init.addr} {' '.join(conds_init)} run function {func_name}")
155
- runcommand(f"execute if score {ret_temp_init.addr} matches 1 run return 1")
157
+ runcommand(f"execute store result score {ret_temp_init._addr} {' '.join(conds_init)} run function {func_name}")
158
+ runcommand(f"execute if score {ret_temp_init._addr} matches 1 run return 1")
156
159
 
157
160
  if orelse_func:
158
161
  if has_break:
@@ -185,7 +188,6 @@ def _flare_with(*args):
185
188
  if hasattr(obj, "__with__"):
186
189
  obj.__with__(lambda: wrap(idx + 1))
187
190
  elif isinstance(obj, str):
188
- from .execute_modifiers import ExecuteChain
189
191
  ExecuteChain(obj).__with__(lambda: wrap(idx + 1))
190
192
  else:
191
193
  raise TypeError(f"Object of type {type(obj).__name__} does not support __with__")
@@ -64,7 +64,7 @@ class ExecuteChain:
64
64
  def store(self, target: Union[score, nbt, str]) -> ExecuteChain:
65
65
  if isinstance(target, score):
66
66
  target._check_addr()
67
- return self._add(f"store result score {target.addr}")
67
+ return self._add(f"store result score {target._addr}")
68
68
  elif isinstance(target, nbt):
69
69
  target._check_addr()
70
70
  return StoreExecuteChain(self.fragments.copy(), target)
@@ -101,23 +101,23 @@ class ExecuteChain:
101
101
  ret_temp = score(addr=f"!ret{ctx._temp_id} {ctx.temp_obj}")
102
102
  ctx._temp_id += 1
103
103
  if prefix.startswith("execute "):
104
- ctx.runcommand(f"execute store result score {ret_temp.addr} {prefix[8:]} run function {func_name}")
104
+ ctx.runcommand(f"execute store result score {ret_temp._addr} {prefix[8:]} run function {func_name}")
105
105
  else:
106
- ctx.runcommand(f"execute store result score {ret_temp.addr} run function {func_name}")
107
- ctx.runcommand(f"execute if score {ret_temp.addr} matches 1 run return 1")
106
+ ctx.runcommand(f"execute store result score {ret_temp._addr} run function {func_name}")
107
+ ctx.runcommand(f"execute if score {ret_temp._addr} matches 1 run return 1")
108
108
 
109
109
 
110
110
  class StoreExecuteChain(ExecuteChain):
111
111
  def __init__(self, fragments: list[str], target: nbt):
112
112
  super().__init__("")
113
113
  self.fragments = fragments
114
- self.target = target
115
- self._datatype = target.type.name.lower() if target.type else "double"
114
+ self._target = target
115
+ self._datatype = target._type.name.lower() if target._type else "double"
116
116
  self._multiplier = 1.0
117
117
  self._update_frag()
118
118
 
119
119
  def _update_frag(self):
120
- frag = f"store result storage {self.target.target} {self.target.path} {self._datatype} {self._multiplier}"
120
+ frag = f"store result storage {self._target.target} {self._target.path} {self._datatype} {self._multiplier}"
121
121
  if self.fragments and self.fragments[-1].startswith("store result "):
122
122
  self.fragments[-1] = frag
123
123
  else:
@@ -59,7 +59,28 @@ class FlareTransformer(ast.NodeTransformer):
59
59
  self.counter += 1
60
60
  return f"__flare_{self.counter}"
61
61
 
62
+ def visit_FunctionDef(self, node):
63
+ is_exported = any(
64
+ isinstance(dec, ast.Name) and dec.id in ("export", "macro") or isinstance(dec, ast.Call) and getattr(
65
+ dec.func, "id", "") in ("export", "macro") for dec in node.decorator_list)
66
+
67
+ is_generated = node.name.startswith("__flare_")
68
+ prev_in_flare = getattr(self, "in_flare_func", False)
69
+
70
+ if is_exported or is_generated:
71
+ self.in_flare_func = True
72
+ else:
73
+ self.in_flare_func = False
74
+
75
+ self.generic_visit(node)
76
+ self.in_flare_func = prev_in_flare
77
+ return node
78
+
62
79
  def visit_If(self, node):
80
+ if not getattr(self, "in_flare_func", False):
81
+ self.generic_visit(node)
82
+ return node
83
+
63
84
  funcs = []
64
85
  cond_args = []
65
86
  body_args = []
@@ -106,11 +127,15 @@ class FlareTransformer(ast.NodeTransformer):
106
127
  return funcs
107
128
 
108
129
  def visit_Break(self, node):
130
+ if not getattr(self, "in_flare_func", False):
131
+ return node
109
132
  call_expr = ast.Expr(value=ast.Call(func=ast.Name(id="_flare_break", ctx=ast.Load()), args=[], keywords=[]))
110
133
  ast.copy_location(call_expr, node)
111
134
  return call_expr
112
135
 
113
136
  def visit_Continue(self, node):
137
+ if not getattr(self, "in_flare_func", False):
138
+ return node
114
139
  call_expr = ast.Expr(value=ast.Call(func=ast.Name(id="_flare_continue", ctx=ast.Load()), args=[], keywords=[]))
115
140
  ast.copy_location(call_expr, node)
116
141
  return call_expr
@@ -120,6 +145,10 @@ class FlareTransformer(ast.NodeTransformer):
120
145
  return node
121
146
 
122
147
  def visit_While(self, node):
148
+ if not getattr(self, "in_flare_func", False):
149
+ self.generic_visit(node)
150
+ return node
151
+
123
152
  class BreakContinueVisitor(ast.NodeVisitor):
124
153
  def __init__(self):
125
154
  self.has_break = False
@@ -184,6 +213,10 @@ class FlareTransformer(ast.NodeTransformer):
184
213
  return funcs
185
214
 
186
215
  def visit_For(self, node):
216
+ if not getattr(self, "in_flare_func", False):
217
+ self.generic_visit(node)
218
+ return node
219
+
187
220
  class BreakContinueVisitor(ast.NodeVisitor):
188
221
  def __init__(self):
189
222
  self.has_break = False
@@ -286,20 +319,28 @@ class FlareTransformer(ast.NodeTransformer):
286
319
  return node
287
320
 
288
321
  def visit_Return(self, node):
322
+ if not getattr(self, "in_flare_func", False):
323
+ self.generic_visit(node)
324
+ return node
325
+
289
326
  self.generic_visit(node)
290
327
 
291
328
  value = node.value if node.value is not None else ast.Constant(value=None)
292
329
 
293
- call_expr = ast.Expr(
294
- value=ast.Call(func=ast.Name(id="_flare_return", ctx=ast.Load()), args=[value], keywords=[]))
295
- ast.copy_location(call_expr, node)
296
-
297
- new_return = ast.Return(value=None)
298
- ast.copy_location(new_return, node)
330
+ if_node = ast.If(test=ast.Compare(
331
+ left=ast.Attribute(value=ast.Name(id="ctx", ctx=ast.Load()), attr="current_file", ctx=ast.Load()),
332
+ ops=[ast.IsNot()], comparators=[ast.Constant(value=None)]), body=[
333
+ ast.Expr(value=ast.Call(func=ast.Name(id="_flare_return", ctx=ast.Load()), args=[value], keywords=[])),
334
+ ast.Return(value=None)], orelse=[ast.Return(value=value)])
335
+ ast.copy_location(if_node, node)
299
336
 
300
- return [call_expr, new_return]
337
+ return if_node
301
338
 
302
339
  def visit_With(self, node):
340
+ if not getattr(self, "in_flare_func", False):
341
+ self.generic_visit(node)
342
+ return node
343
+
303
344
  self.generic_visit(node)
304
345
 
305
346
  name_body = self.gen_name()
@@ -414,6 +455,10 @@ def preprocess_minecraft_commands(source: str) -> str:
414
455
 
415
456
  match = COMMAND_RE.match(line)
416
457
  if match:
458
+ if re.match(r"^\s*(?:" + COMMAND_KEYWORDS + r")\s*(?:[+\-*/%&|^]?=|\()", line):
459
+ i += 1
460
+ continue
461
+
417
462
  indent = match.group(1)
418
463
  cmd = match.group(2) + match.group(3)
419
464
  if cmd.startswith("/"):