Skip to content

Commit 6e52bee

Browse files
committed
Implement "entity local" feature - variables attached to a specific entity
1 parent 8cf42cb commit 6e52bee

File tree

8 files changed

+141
-28
lines changed

8 files changed

+141
-28
lines changed

asm_reader.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ def __next__(self):
6060
const = self.next_constant()
6161
self.end_of_line()
6262
return const
63+
elif char == '@':
64+
self.skip(1)
65+
ent_local = self.next_entity_local()
66+
self.end_of_line()
67+
return ent_local
6368
elif char == '_':
6469
self.skip(1)
6570
return self.next_local_label()
@@ -106,6 +111,10 @@ def next_constant(self):
106111
value = self.read_ref()
107112
return ('const', (name, value))
108113

114+
def next_entity_local(self):
115+
name = self.read_symbol()
116+
return ('entity_local', name)
117+
109118
def next_local_label(self):
110119
return ('local_label', self.read_label())
111120

assembler.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def parse(self, text, filename=''):
4545
if token == 'const':
4646
name, ref = arg
4747
self.define_const(name, self.resolve_ref(*ref))
48+
elif token == 'entity_local':
49+
self.define_entity_local(arg)
4850
elif token == 'label':
4951
self.handle_label(arg)
5052
elif token == 'instruction':
@@ -70,6 +72,9 @@ def define_const(self, name, value):
7072
raise RuntimeError('Constant %r already defined' % name)
7173
self.constants[name] = value
7274

75+
def define_entity_local(self, name):
76+
self.define_const(name, EntityLocal(name))
77+
7378
def get_const(self, name):
7479
return self.constants[name]
7580

@@ -180,11 +185,11 @@ def add_cast(self, type, cmd, dest):
180185
}[type]
181186
# store result of cmd into nbt of the specified type
182187
self.add_command(ExecuteChain() \
183-
.store('result').entity(data_type).run(cmd))
188+
.store('result').entity(EntityTag, data_type).run(cmd))
184189
# move nbt value back out to scoreboard
185190
self.add_command(ExecuteChain() \
186191
.store('result').score(EntityTag, dest) \
187-
.run(DataGet(data_type)))
192+
.run(DataGet(EntityTag, data_type)))
188193

189194

190195
def handle_directive(self, directive, value):

commands.py

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ def resolve(self, scope):
4949
pass
5050

5151
class Ref(Resolvable):
52-
pass
52+
53+
def selector(self, where):
54+
return ETagSelector(where)
5355

5456
class Var(Ref):
5557
def __init__(self, nameref, *args):
@@ -66,6 +68,16 @@ def __init__(self, loc):
6668
def resolve(self, scope):
6769
return scope.memory(self.loc)
6870

71+
class EntityLocal(Ref):
72+
def __init__(self, name):
73+
self.name = name
74+
75+
def resolve(self, scope):
76+
return scope.entity_local(self.name)
77+
78+
def selector(self, where):
79+
return Selector('s', where)
80+
6981
class SimpleResolve(Resolvable):
7082

7183
def __init__(self, *args):
@@ -101,14 +113,29 @@ def str_pairs(items):
101113

102114
class Selector(Resolvable):
103115

104-
def __init__(self, where):
116+
def __init__(self, type, where):
117+
self.type = type
105118
self.where = where
106119

120+
def resolve_params(self, scope):
121+
if not self.where:
122+
return {}
123+
return self.where.resolve(scope)
124+
107125
def resolve(self, scope):
108-
where = {} if not self.where else self.where.resolve(scope)
109-
return make_selector('e', tag=scope.entity_tag, limit=1, **where)
126+
return make_selector(self.type, **self.resolve_params(scope))
127+
128+
class ETagSelector(Selector):
129+
130+
def __init__(self, where):
131+
super().__init__('e', where)
110132

111-
EntityTag = Selector(None)
133+
def resolve_params(self, scope):
134+
where = super().resolve_params(scope)
135+
where.update(tag=scope.entity_tag, limit=1)
136+
return where
137+
138+
EntityTag = ETagSelector(None)
112139

113140
class Path(Resolvable):
114141

@@ -161,6 +188,10 @@ def resolve(self, scope):
161188
return 'execute %s run %s' % (self.chain.resolve(scope),
162189
self.command.resolve(scope))
163190

191+
def ensure_selector(sel_arg):
192+
return sel_arg.as_selector() if isinstance(sel_arg, SelectorArgs) \
193+
else sel_arg
194+
164195
class ExecuteChain:
165196

166197
def __init__(self):
@@ -188,7 +219,7 @@ def if_where(cond):
188219
return ExecuteChain().cond('if').where(cond)
189220

190221
def where(self, select_arg):
191-
return self.add('as', Selector(select_arg))
222+
return self.add('as', ensure_selector(select_arg))
192223

193224
def cond(self, cond_type):
194225
return ExecuteChain.Cond(self, cond_type)
@@ -203,7 +234,7 @@ def __init__(self, parent, cond_type):
203234
self.cond_type = cond_type
204235

205236
def where(self, select_arg):
206-
return self.add('entity', Selector(select_arg))
237+
return self.add('entity', ensure_selector(select_arg))
207238

208239
def score(self, target, t_objective, operator, source, s_objective):
209240
return self.add('score', target, t_objective, operator, source,
@@ -225,18 +256,20 @@ def __init__(self, parent, store_type):
225256
self.store_type = store_type
226257

227258
def score(self, name, objective):
228-
return self.add('score', name, objective)
259+
return self.add('score', ensure_selector(name), objective)
229260

230-
def entity(self, data_type):
231-
return self.add('entity', EntityTag, Path(data_type), data_type, 1)
261+
def entity(self, target, data_type):
262+
return self.add('entity', ensure_selector(target), \
263+
Path(data_type), data_type, 1)
232264

233265
class DataGet(Command):
234266

235-
def __init__(self, path):
267+
def __init__(self, target, path):
268+
self.target = target
236269
self.path = Path(path)
237270

238271
def resolve(self, scope):
239-
return 'data get entity %s %s' % (EntityTag.resolve(scope),
272+
return 'data get entity %s %s' % (self.target.resolve(scope),
240273
self.path.resolve(scope))
241274

242275
class Function(Command):
@@ -290,7 +323,7 @@ def __init__(self, varref, value, where=None):
290323
assert self.allows_negative or value >= 0
291324
self.var = varref
292325
self.value = value
293-
self.selector = Selector(where)
326+
self.selector = varref.selector(where)
294327

295328
def resolve(self, scope):
296329
return 'scoreboard players %s %s %s %d' % (
@@ -331,13 +364,13 @@ def __init__(self, left, right, where=None):
331364
assert isinstance(right, Ref)
332365
self.left = left
333366
self.right = right
334-
self.selector = Selector(where)
367+
self.left_sel = left.selector(where)
368+
self.right_sel = right.selector(where)
335369

336370
def resolve(self, scope):
337-
selector = self.selector.resolve(scope)
338371
return 'scoreboard players operation %s %s %s %s %s' % (
339-
selector, self.left.resolve(scope), self.op,
340-
selector, self.right.resolve(scope))
372+
self.left_sel.resolve(scope), self.left.resolve(scope), self.op,
373+
self.right_sel.resolve(scope), self.right.resolve(scope))
341374

342375

343376
class OpAssign(Operation): op = '='
@@ -351,7 +384,16 @@ class OpIfGt(Operation): op = '>'
351384
class OpSwap(Operation): op = '><'
352385

353386
class SelectorArgs(Resolvable):
354-
pass
387+
388+
def as_selector(self):
389+
return ETagSelector(self)
390+
391+
class SimpleSelectorArgs(SelectorArgs):
392+
def __init__(self, args):
393+
self.args = args
394+
395+
def resolve(self, scope):
396+
return dict(self.args)
355397

356398
class SelRange(SelectorArgs):
357399
def __init__(self, varref, min=None, max=None):
@@ -361,6 +403,9 @@ def __init__(self, varref, min=None, max=None):
361403
self.min = min
362404
self.max = max
363405

406+
def as_selector(self):
407+
return self.var.selector(self)
408+
364409
def resolve(self, scope):
365410
name = self.var.resolve(scope)
366411
range = ''

compiler/asm_writer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ def write_constant(self, name, value):
2525
def write_directive(self, name, value):
2626
self.write_line('#%s %s' % (name, value))
2727

28+
def write_entity_local(self, name):
29+
self.write_line('@%s' % name)
30+
2831
def write_subroutine(self, name):
2932
if name == '__setup__':
3033
return

compiler/compiler.py

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def compile_program(self, program):
2020
*(self.temp_registers + list(self.volatile_reg)))
2121
self.writer.write_instruction('MOV', '#0', '0')
2222
self.writer.write_instruction('MOV', '#0', '1')
23+
self.entity_locals = {}
2324
self.optimizer = Optimizer(self)
2425
visitor = CompilerVisitor(self.optimizer.handle_insn)
2526
self.load_libs(visitor)
@@ -61,6 +62,10 @@ def handle_fn_end(self, insn):
6162
def handle_label(self, insn):
6263
self.writer.write_local_sub(insn.label)
6364

65+
def handle_entity_local(self, insn):
66+
self.entity_locals[insn.offset] = insn.name
67+
self.writer.write_entity_local(insn.name)
68+
6469
def handle_jump(self, insn):
6570
self.writer.write_instruction('JMP', self.label(insn.dest))
6671

@@ -182,8 +187,8 @@ def handle_operation(self, insn):
182187
self.writer.write_instruction(opcode, right, tmp_ref)
183188
self._write_relative_move(tmp_ref, None, d_ref, d_off)
184189
self.free_slot(tmp_slot)
185-
elif insn.op in [IR.Op.Eq, IR.Op.Neq, IR.Op.Lt,
186-
IR.Op.Gt, IR.Op.LtEq, IR.Op.GtEq]:
190+
elif insn.op.__class__ in map(lambda o:o.__class__, [IR.Op.Eq,
191+
IR.Op.Neq, IR.Op.Lt, IR.Op.Gt, IR.Op.LtEq, IR.Op.GtEq]):
187192
jmpcode = {
188193
IR.Op.Eq.__class__: 'JE',
189194
IR.Op.Neq.__class__: 'JNE',
@@ -202,7 +207,17 @@ def handle_operation(self, insn):
202207
self._write_relative_move('#0', None, dest, dest_off)
203208
self.writer.write_local_sub(lbl)
204209
elif insn.op is IR.Op.LogAnd:
205-
assert False, str(insn)
210+
left = self.get_final_location(insn.left)
211+
right = self.get_final_location(insn.right)
212+
dest, dest_off = self.get_effective_location(insn.dest)
213+
self._write_relative_move('#0', None, dest, dest_off)
214+
self.writer.write_instruction('CMP', left, '#0')
215+
lbl = 'and_end_%d' % abs(hash(insn)) # TODO
216+
self.writer.write_instruction('JE', '_' + lbl)
217+
self.writer.write_instruction('CMP', right, '#0')
218+
self.writer.write_instruction('JE', '_' + lbl)
219+
self._write_relative_move('#1', None, dest, dest_off)
220+
self.writer.write_local_sub(lbl)
206221
elif insn.op is IR.Op.LogOr:
207222
assert False, str(insn)
208223
else:
@@ -323,6 +338,8 @@ def unpack(self, ref):
323338
return base, 0
324339
if isinstance(ref, IR.GlobalSlot):
325340
return None, ref.offset
341+
if isinstance(ref, IR.EntityLocalSlot):
342+
return self.entity_locals[ref.offset], None
326343
assert False, "Don't know how to handle " + str(ref)
327344

328345
def __next_volatile(self):
@@ -414,12 +431,23 @@ def __str__(self):
414431
def relative_base(self):
415432
return IR.StackPointer
416433

434+
class EntityLocalStorage(Storage):
435+
436+
def __str__(self):
437+
return 'EntityLocal'
438+
439+
@property
440+
def relative_base(self):
441+
return IR.EntityLocalBase
442+
417443
class ScopeManager:
418444

419-
def __init__(self):
445+
def __init__(self, compiler):
446+
self.compiler = compiler
420447
self.global_table = SymbolTable()
421448
self.current_table = self.global_table
422449
self.global_storage = GlobalStorage()
450+
self.entity_local_storage = EntityLocalStorage()
423451
self.current_storage = self.global_storage
424452

425453
def __enter__(self):
@@ -443,7 +471,13 @@ def lookup(self, name):
443471
return self.current_table.lookup(name)
444472

445473
def declare_symbol(self, name, type, can_redeclare=False):
446-
return self.current_table.declare(name, type, self.store(type),
474+
if type is self.compiler.types.types['entity_local']:
475+
# hijack entity_local
476+
unit = self.entity_local_storage.insert(type)
477+
self.compiler.emit(IR.EntityLocalVar(name, unit.offset))
478+
else:
479+
unit = self.store(type)
480+
return self.current_table.declare(name, type, unit,
447481
can_redeclare)
448482

449483
def store(self, type):
@@ -475,7 +509,9 @@ def __init__(self, on_instruction):
475509
super().__init__()
476510
self.on_instruction = on_instruction
477511
self.types = Types()
478-
self.scope = ScopeManager()
512+
self.types.add_type('entity_local', DecoratedType(IntType(),
513+
static=True, const=False))
514+
self.scope = ScopeManager(self)
479515
self.current_function = None
480516
self.loop_attacher = LoopAttacher(None, None, self, None)
481517
self.python_functions = {}
@@ -489,6 +525,9 @@ def eliminate_offset(self, slot):
489525
if slot.base is IR.GlobalIndex:
490526
assert isinstance(slot.offset, IR.LiteralInt), str(slot)
491527
return IR.GlobalSlot(slot.offset.val, slot.type)
528+
if slot.base is IR.EntityLocalBase:
529+
assert isinstance(slot.offset, IR.LiteralInt), str(slot)
530+
return IR.EntityLocalSlot(slot.offset.val, slot.type)
492531
base = self.eliminate_offset(slot.base)
493532
if isinstance(base, IR.GlobalSlot):
494533
assert isinstance(slot.offset, IR.LiteralInt), str(slot)

compiler/ir.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class IR:
1010
FunctionEnd = namedtuple('FunctionEnd', '')
1111

1212
Label = namedtuple('Label', 'label')
13+
EntityLocalVar = namedtuple('EntityLocalVar', 'name offset')
1314

1415
Jump = namedtuple('Jump', 'dest')
1516
JumpIf = namedtuple('JumpIf', 'dest cond')
@@ -30,10 +31,12 @@ class IR:
3031
StackPointer = namedtuple('StackPointer', 'type')(IntType())
3132
BasePointer = namedtuple('BasePointer', 'type')(IntType())
3233
GlobalIndex = namedtuple('GlobalIndex', 'type')(IntType())
34+
EntityLocalBase = namedtuple('EntityLocalBase', 'type')(IntType())
3335
ReturnRegister = namedtuple('ReturnRegister', 'type')(IntType())
3436

3537
SlotOffset = namedtuple('SlotOffset', 'base offset type')
3638
GlobalSlot = namedtuple('GlobalSlot', 'offset type')
39+
EntityLocalSlot = namedtuple('EntityLocalSlot', 'offset type')
3740
Dereference = namedtuple('Dereference', 'addr type')
3841
Free = namedtuple('Free', 'slot')
3942

@@ -120,6 +123,7 @@ def __init__(self, downstream=None):
120123
IR.FunctionBegin: self.handle_fn_begin,
121124
IR.FunctionEnd: self.handle_fn_end,
122125
IR.Label: self.handle_label,
126+
IR.EntityLocalVar: self.handle_entity_local,
123127
IR.Jump: self.handle_jump,
124128
IR.JumpIf: self.handle_jump_if,
125129
IR.JumpIfNot: self.handle_jump_if_not,
@@ -154,6 +158,9 @@ def handle_fn_end(self, insn):
154158
def handle_label(self, insn):
155159
self.emit(insn)
156160

161+
def handle_entity_local(self, insn):
162+
self.emit(insn)
163+
157164
def handle_jump(self, insn):
158165
self.emit(insn)
159166

0 commit comments

Comments
 (0)