Integrate marking labels/slots
This commit is contained in:
parent
99f456e0ee
commit
a79644aa23
3 changed files with 76 additions and 37 deletions
|
@ -25,6 +25,7 @@ from shutil import copy2 as copyfile
|
||||||
import stat
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import typing as t
|
import typing as t
|
||||||
|
import os
|
||||||
|
|
||||||
from .util import *
|
from .util import *
|
||||||
|
|
||||||
|
@ -350,7 +351,7 @@ def img_info(p: Path) -> ImgInfo:
|
||||||
|
|
||||||
def is_readonly(src: Path) -> bool:
|
def is_readonly(src: Path) -> bool:
|
||||||
statinfo = os.stat(src, dir_fd=None, follow_symlinks=True)
|
statinfo = os.stat(src, dir_fd=None, follow_symlinks=True)
|
||||||
if statinfo.st_flags & stat.UF_IMMUTABLE:
|
if getattr(statinfo, 'st_flags', 0) & stat.UF_IMMUTABLE:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -364,8 +365,11 @@ def main():
|
||||||
if is_readonly(src):
|
if is_readonly(src):
|
||||||
print(f" warning: {src} is read-only, unable to remove")
|
print(f" warning: {src} is read-only, unable to remove")
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
src.unlink()
|
src.unlink()
|
||||||
print(" unlink: ok")
|
print(" unlink: ok")
|
||||||
|
except PermissionError:
|
||||||
|
print(" unlink: fail")
|
||||||
|
|
||||||
def _copy(src, target):
|
def _copy(src, target):
|
||||||
print(f" rename: {target}")
|
print(f" rename: {target}")
|
||||||
|
@ -398,8 +402,7 @@ def main():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif ext in ["thm", "lrv", "ico", "sav"] or src.name.startswith("._"):
|
elif ext in ["thm", "lrv", "ico", "sav"] or src.name.startswith("._"):
|
||||||
if opts.destructive:
|
_delete(src)
|
||||||
src.unlink()
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif ext in KNOWN_IMG_TYPES:
|
elif ext in KNOWN_IMG_TYPES:
|
||||||
|
@ -437,11 +440,7 @@ def main():
|
||||||
elif checksum_path_blocks(src) == checksum_path_blocks(target):
|
elif checksum_path_blocks(src) == checksum_path_blocks(target):
|
||||||
print(f" ok: {target}")
|
print(f" ok: {target}")
|
||||||
# src != target && id(src) == id(target); delete src
|
# src != target && id(src) == id(target); delete src
|
||||||
if opts.destructive:
|
_delete(src)
|
||||||
if is_readonly(src):
|
|
||||||
print(f" warning: {src} is read-only, unable to remove")
|
|
||||||
else:
|
|
||||||
src.unlink()
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# src != target && id(src) != id(target); replace target with src?
|
# src != target && id(src) != id(target); replace target with src?
|
||||||
|
|
|
@ -15,10 +15,39 @@ def gensym(prefix = None) -> isa.Label:
|
||||||
class FuncBuilder(object):
|
class FuncBuilder(object):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._opcodes = []
|
self._opcodes = []
|
||||||
|
self._slots = {}
|
||||||
|
self._stack = 0
|
||||||
|
self._straightline = True
|
||||||
|
|
||||||
def _write(self, op):
|
def _write(self, op):
|
||||||
self._opcodes.append(op)
|
self._opcodes.append(op)
|
||||||
|
|
||||||
|
# Bookkeeping for statically analyzing the stack
|
||||||
|
if not self._straightline:
|
||||||
|
return
|
||||||
|
|
||||||
|
match op:
|
||||||
|
case isa.Label(_):
|
||||||
|
# Labels abort
|
||||||
|
self._straightline = False
|
||||||
|
|
||||||
|
case isa.GOTO(_) | isa.ATEST(_):
|
||||||
|
# Control ops abort
|
||||||
|
self._straightline = False
|
||||||
|
|
||||||
|
case isa.DROP(n):
|
||||||
|
self._stack -= n
|
||||||
|
|
||||||
|
case isa.DUP(n):
|
||||||
|
self._stack += n
|
||||||
|
|
||||||
|
case isa.ROT(_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
case _:
|
||||||
|
self._stack -= getattr(op, 'nargs', 0)
|
||||||
|
self._stack += 1
|
||||||
|
|
||||||
def write(self, op: Union[isa.Opcode, isa.Label, Sequence[isa.Opcode]]):
|
def write(self, op: Union[isa.Opcode, isa.Label, Sequence[isa.Opcode]]):
|
||||||
|
|
||||||
def flatten(o):
|
def flatten(o):
|
||||||
|
@ -30,6 +59,7 @@ class FuncBuilder(object):
|
||||||
|
|
||||||
if isinstance(op, (isa.Opcode, isa.Label)):
|
if isinstance(op, (isa.Opcode, isa.Label)):
|
||||||
self._opcodes.append(op)
|
self._opcodes.append(op)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for e in op:
|
for e in op:
|
||||||
self.write(e)
|
self.write(e)
|
||||||
|
@ -37,6 +67,26 @@ class FuncBuilder(object):
|
||||||
def make_label(self, prefix: Optional[str] = ""):
|
def make_label(self, prefix: Optional[str] = ""):
|
||||||
return gensym(prefix)
|
return gensym(prefix)
|
||||||
|
|
||||||
|
def mark_label(self, prefix: Optional[str] = ""):
|
||||||
|
l = self.make_label(prefix=prefix)
|
||||||
|
self.write(l)
|
||||||
|
return l
|
||||||
|
|
||||||
|
def mark_slot(self, target: Optional[int] = None, prefix: Optional[str] = ""):
|
||||||
|
if target is not None:
|
||||||
|
s = gensym(prefix)
|
||||||
|
self._slots[s] = target
|
||||||
|
return s
|
||||||
|
|
||||||
|
elif self._straightline:
|
||||||
|
s = gensym(prefix)
|
||||||
|
self._slots[s] = self._stack
|
||||||
|
return s
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception("Can't automatically generate slots after control flow (label/goto), provide `target=`.")
|
||||||
|
|
||||||
|
|
||||||
def build(self) -> List[isa.Opcode]:
|
def build(self) -> List[isa.Opcode]:
|
||||||
"""Assemble the written body into fully resolved opcodes."""
|
"""Assemble the written body into fully resolved opcodes."""
|
||||||
|
|
||||||
|
@ -64,6 +114,9 @@ class FuncBuilder(object):
|
||||||
case isa.ATEST(isa.Label(_) as l):
|
case isa.ATEST(isa.Label(_) as l):
|
||||||
assembled.append(isa.ATEST(labels[l]))
|
assembled.append(isa.ATEST(labels[l]))
|
||||||
|
|
||||||
|
case isa.SLOT(isa.Label(_) as l):
|
||||||
|
assembled.append(isa.SLOT(self._slots[l]))
|
||||||
|
|
||||||
case o:
|
case o:
|
||||||
assembled.append(o)
|
assembled.append(o)
|
||||||
|
|
||||||
|
@ -75,30 +128,3 @@ def assemble(builder_cls=FuncBuilder, /, **opcodes: List[isa.Opcode]) -> List[is
|
||||||
for o in opcodes:
|
for o in opcodes:
|
||||||
builder.write(o)
|
builder.write(o)
|
||||||
return builder.build()
|
return builder.build()
|
||||||
|
|
||||||
|
|
||||||
class LocalBuilder(FuncBuilder):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self._stack = 0
|
|
||||||
self._labels = {}
|
|
||||||
|
|
||||||
def _write(self, op):
|
|
||||||
super()._write(op)
|
|
||||||
|
|
||||||
match op:
|
|
||||||
case isa.DROP(n):
|
|
||||||
self._stack -= n
|
|
||||||
case isa.DUP(n):
|
|
||||||
self._stack += n
|
|
||||||
case isa.ROT(_):
|
|
||||||
pass
|
|
||||||
case _:
|
|
||||||
self._stack -= getattr(op, 'nargs', 0)
|
|
||||||
self._stack += 1
|
|
||||||
|
|
||||||
def write_local(self, label: isa.Label):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_local(self, label: isa.Label):
|
|
||||||
pass
|
|
||||||
|
|
|
@ -46,3 +46,17 @@ def test_self_label(builder: FuncBuilder):
|
||||||
isa.DROP(0),
|
isa.DROP(0),
|
||||||
isa.GOTO(1),
|
isa.GOTO(1),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_label(builder: FuncBuilder):
|
||||||
|
s0 = builder.mark_slot()
|
||||||
|
builder.write(isa.SLOT(s0))
|
||||||
|
|
||||||
|
s999 = builder.mark_slot(target=999)
|
||||||
|
builder.write(isa.SLOT(s999))
|
||||||
|
|
||||||
|
instrs = builder.build()
|
||||||
|
assert instrs == [
|
||||||
|
isa.SLOT(0),
|
||||||
|
isa.SLOT(999),
|
||||||
|
]
|
||||||
|
|
Loading…
Reference in a new issue