Rewrite lua_eval result processing

This commit is contained in:
neumond 2020-07-21 01:32:37 +03:00
parent cb66ca9741
commit 1b5c81679e
31 changed files with 541 additions and 425 deletions

View file

@ -6,6 +6,7 @@ local url = 'http://127.0.0.1:4343/'
local tasks = {}
local filters = {}
local ycounts = {}
local coparams = {}
ws = http.websocket(url..'ws/')
if ws == false then
@ -117,12 +118,13 @@ while true do
ws_send{
action='task_result',
task_id=msg.task_id,
result={fn()},
result={fn(table.unpack(msg.params or {}))},
yields=0,
}
else
tasks[msg.task_id] = coroutine.create(fn)
ycounts[msg.task_id] = 0
coparams[msg.task_id] = msg.params or {}
end
end
elseif msg.action == 'drop' then
@ -130,6 +132,7 @@ while true do
tasks[task_id] = nil
filters[task_id] = nil
ycounts[task_id] = nil
coparams[task_id] = nil
end
elseif msg.action == 'sub' then
event_sub[msg.event] = true
@ -152,7 +155,13 @@ while true do
local del_tasks = {}
for task_id in pairs(tasks) do
if filters[task_id] == nil or filters[task_id] == event then
local r = {coroutine.resume(tasks[task_id], event, p1, p2, p3, p4, p5)}
local r
if coparams[task_id] ~= nil then
r = {coroutine.resume(tasks[task_id], table.unpack(coparams[task_id]))}
coparams[task_id] = nil
else
r = {coroutine.resume(tasks[task_id], event, p1, p2, p3, p4, p5)}
end
if coroutine.status(tasks[task_id]) == 'dead' then
ws_send{
action='task_result',
@ -175,6 +184,7 @@ while true do
tasks[task_id] = nil
filters[task_id] = nil
ycounts[task_id] = nil
coparams[task_id] = nil
end
end

View file

@ -58,6 +58,8 @@ def lua_value(v):
return 'true'
if isinstance(v, str):
return lua_string(v)
if isinstance(v, bytes):
return lua_string(v.decode('latin1'))
if isinstance(v, (int, float)):
return str(v)
if isinstance(v, list):

View file

@ -1,6 +1,155 @@
from .errors import LuaException
def lua_table_to_list(x, length: int = None):
if not x:
return [] if length is None else [None] * length
assert all(map(lambda k: isinstance(k, int), x.keys()))
assert min(x.keys()) >= 1
if length is not None:
assert max(x.keys()) <= length
else:
length = max(x.keys())
return [x.get(i + 1) for i in range(length)]
class ResultProc:
def __init__(self, result):
self._v = result
self._i = 1
def forward(self):
self._i += 1
def back(self):
self._i -= 1
def peek(self):
return self._v.get(self._i)
def take(self):
r = self.peek()
self.forward()
return r
def take_none(self):
x = self.take()
assert x is None
return x
def take_bool(self):
x = self.take()
assert x is True or x is False
return x
def take_int(self):
x = self.take()
assert isinstance(x, int)
assert not isinstance(x, bool)
return x
def take_number(self):
x = self.take()
assert isinstance(x, (int, float))
assert not isinstance(x, bool)
return x
def take_bytes(self):
x = self.take()
assert isinstance(x, bytes)
return x
def take_string(self):
return self.take_bytes().decode('latin1')
def take_unicode(self):
return self.take_bytes().decode('utf-8')
def take_dict(self, keys=None):
x = self.take()
assert isinstance(x, dict)
if keys is None:
return x
return TableProc(x, keys)
def take_list(self, length: int = None):
return lua_table_to_list(self.take_dict(), length)
def check_bool_error(self):
success = self.take_bool()
if not success:
raise LuaException(self.take_string())
def check_nil_error(self):
if self.peek() is None:
self.forward()
raise LuaException(self.take_string())
def bool_error_exclude(self, exc_msg):
success = self.take_bool()
if success:
return True
msg = self.take_string()
if msg == exc_msg:
return False
raise LuaException(msg)
def take_option_int(self):
if self.peek() is None:
return self.take_none()
return self.take_int()
def take_option_bytes(self):
if self.peek() is None:
return self.take_none()
return self.take_bytes()
def take_option_string(self):
if self.peek() is None:
return self.take_none()
return self.take_string()
def take_option_unicode(self):
if self.peek() is None:
return self.take_none()
return self.take_unicode()
def take_option_string_bool(self):
p = self.peek()
if p is None or p is True or p is False:
self.forward()
return p
return self.take_string()
def take_list_of_strings(self, length: int = None):
x = self.take_list(length)
assert all(map(lambda v: isinstance(v, bytes), x))
return [v.decode('latin1') for v in x]
def take_list_of_ints(self):
x = self.take_list()
assert all(map(lambda v: isinstance(v, int), x))
return x
def take_2d_int(self):
x = self.take_list()
x = [lua_table_to_list(item) for item in x]
for row in x:
for item in row:
assert isinstance(item, int)
return x
class TableProc(ResultProc):
def __init__(self, result, keys):
self._v = result
self._keys = keys
self._i = 0
def peek(self):
return self._v.get(self._keys[self._i])
def coro(result):
assert isinstance(result, list)
assert len(result) >= 1

View file

@ -21,6 +21,8 @@ def serialize(v: Any) -> bytes:
return b'T'
elif isinstance(v, (int, float)):
return '[{}]'.format(v).encode(_ENC)
elif isinstance(v, bytes):
return '<{}>'.format(len(v)) + v
elif isinstance(v, str):
v = v.encode(_ENC)
return '<{}>'.format(len(v)).encode(_ENC) + v
@ -56,7 +58,7 @@ def _deserialize(b: bytes, _idx: int) -> Tuple[Any, int]:
elif tok == 60: # <
newidx = b.index(b'>', _idx)
ln = int(b[_idx:newidx].decode(_ENC))
return b[newidx + 1:newidx + 1 + ln].decode(_ENC), newidx + 1 + ln
return b[newidx + 1:newidx + 1 + ln], newidx + 1 + ln
elif tok == 123: # {
r = {}
while True:
@ -67,12 +69,6 @@ def _deserialize(b: bytes, _idx: int) -> Tuple[Any, int]:
key, _idx = _deserialize(b, _idx)
value, _idx = _deserialize(b, _idx)
r[key] = value
if r:
for i in range(1, len(r) + 1):
if i not in r:
break
else:
r = [r[i + 1] for i in range(len(r))]
return r, _idx
else:
raise ValueError

View file

@ -25,7 +25,7 @@ class CCApplication(web.Application):
async def _launch_program(self, ws):
async for msg in self._bin_messages(ws):
msg = ser.deserialize(msg)
if msg['action'] != 'run':
if msg[b'action'] != b'run':
await ws.send_bytes(ser.serialize({
'action': 'close',
'error': 'protocol error',
@ -36,9 +36,9 @@ class CCApplication(web.Application):
sys.__stdout__.write('ws send ' + repr(data) + '\n')
asyncio.create_task(ws.send_bytes(data))
sess = CCSession(msg['computer'], sender)
if msg['args']:
sess.run_program(msg['args'][0])
sess = CCSession(msg[b'computer'], sender)
if msg[b'args']:
sess.run_program(msg[b'args'][1].decode('latin1'))
else:
sess.run_repl()
return sess
@ -51,10 +51,10 @@ class CCApplication(web.Application):
if sess is not None:
async for msg in self._bin_messages(ws):
msg = ser.deserialize(msg)
if msg['action'] == 'event':
sess.on_event(msg['event'], msg['params'])
elif msg['action'] == 'task_result':
sess.on_task_result(msg['task_id'], msg['result'])
if msg[b'action'] == b'event':
sess.on_event(msg[b'event'].decode('latin1'), msg[b'params'])
elif msg[b'action'] == b'task_result':
sess.on_task_result(msg[b'task_id'].decode('latin1'), msg[b'result'])
else:
await ws.send_bytes(ser.serialize({
'action': 'close',

View file

@ -73,22 +73,22 @@ class StdFileProxy:
raise RuntimeError(
"Computercraft environment doesn't support "
"stdin readline method with parameter")
return rproc.string(eval_lua(
return eval_lua(
return_lua_call('io.read')
)) + '\n'
).take_string() + '\n'
def write(self, s):
if _is_global_greenlet():
return self._native.write(s)
else:
if self._err:
return rproc.nil(eval_lua(
return eval_lua(
lua_call('io.stderr:write', s)
))
).take_none()
else:
return rproc.nil(eval_lua(
return eval_lua(
lua_call('io.write', s)
))
).take_none()
def fileno(self):
if _is_global_greenlet():
@ -147,9 +147,10 @@ def eval_lua(lua_code, immediate=False):
})
result = get_current_session()._server_greenlet.switch(request)
# debug('{} → {}'.format(lua_code, repr(result)))
rp = rproc.ResultProc(result)
if not immediate:
result = rproc.coro(result)
return result
rp.check_bool_error()
return rp
@contextmanager
@ -342,7 +343,7 @@ class CCSession:
def run_program(self, program):
def _run_program():
p, code = eval_lua('''
rp = eval_lua('''
local p = fs.combine(shell.dir(), {})
if not fs.exists(p) then return nil end
if fs.isDir(p) then return nil end
@ -351,6 +352,8 @@ local code = f.readAll()
f.close()
return p, code
'''.lstrip().format(lua_string(program)))
p = rp.take_string()
code = rp.take_string()
cc = compile(code, p, 'exec')
exec(cc, {'__file__': p})

View file

@ -2,7 +2,6 @@ from types import ModuleType
from ..errors import LuaException
from ..lua import lua_string
from ..rproc import boolean, option_string
from ..sess import eval_lua
@ -26,7 +25,7 @@ def import_file(path: str, relative_to: str = None):
lua_string(relative_to),
path_expr,
)
source = option_string(eval_lua('''
source = eval_lua('''
local p = {}
if not fs.exists(p) then return nil end
if fs.isDir(p) then return nil end
@ -36,7 +35,7 @@ f.close()
return src
'''.lstrip().format(
path_expr,
)))
)).take_option_string()
if source is None:
raise ImportError('File not found: {}'.format(path))
cc = compile(source, mod.__name__, 'exec')
@ -45,16 +44,16 @@ return src
def is_commands() -> bool:
return boolean(eval_lua('return commands ~= nil'))
return eval_lua('return commands ~= nil').take_bool()
def is_multishell() -> bool:
return boolean(eval_lua('return multishell ~= nil'))
return eval_lua('return multishell ~= nil').take_bool()
def is_turtle() -> bool:
return boolean(eval_lua('return turtle ~= nil'))
return eval_lua('return turtle ~= nil').take_bool()
def is_pocket() -> bool:
return boolean(eval_lua('return pocket ~= nil'))
return eval_lua('return pocket ~= nil').take_bool()

View file

@ -1,6 +1,5 @@
from typing import Tuple
from ..rproc import boolean, integer, tuple3_number
from ..sess import eval_lua_method_factory
@ -55,23 +54,24 @@ black = 0x8000
# combine, subtract and test are mostly for redstone.setBundledOutput
def combine(*colors: int) -> int:
return integer(method('combine', *colors))
return method('combine', *colors).take_int()
def subtract(color_set: int, *colors: int) -> int:
return integer(method('subtract', color_set, *colors))
return method('subtract', color_set, *colors).take_int()
def test(colors: int, color: int) -> bool:
return boolean(method('test', colors, color))
return method('test', colors, color).take_bool()
def packRGB(r: float, g: float, b: float) -> int:
return integer(method('packRGB', r, g, b))
return method('packRGB', r, g, b).take_int()
def unpackRGB(rgb: int) -> Tuple[float, float, float]:
return tuple3_number(method('unpackRGB', rgb))
rp = method('unpackRGB', rgb)
return tuple(rp.take_number() for _ in range(3))
# use these chars for term.blit

View file

@ -1,10 +1,8 @@
from typing import Tuple, List, Optional
from ..rproc import tuple3_integer, any_dict, any_list, array_string, fact_tuple, boolean, option_integer
from ..sess import eval_lua_method_factory
command_result = fact_tuple(boolean, array_string, option_integer, tail_nils=1)
method = eval_lua_method_factory('commands.')
@ -18,20 +16,25 @@ __all__ = (
def exec(command: str) -> Tuple[bool, List[str], Optional[int]]:
return command_result(method('exec', command))
rp = method('exec', command)
success = rp.take_bool()
log = rp.take_list_of_strings()
n = rp.take_option_int()
return success, log, n
def list() -> List[str]:
return array_string(method('list'))
return method('list').take_list_of_strings()
def getBlockPosition() -> Tuple[int, int, int]:
return tuple3_integer(method('getBlockPosition'))
rp = method('getBlockPosition')
return tuple(rp.take_int() for _ in range(3))
def getBlockInfo(x: int, y: int, z: int) -> dict:
return any_dict(method('getBlockInfo', x, y, z))
return method('getBlockInfo', x, y, z).take_dict()
def getBlockInfos(x1: int, y1: int, z1: int, x2: int, y2: int, z2: int) -> List[dict]:
return any_list(method('getBlockInfos', x1, y1, z1, x2, y2, z2))
return method('getBlockInfos', x1, y1, z1, x2, y2, z2).take_list()

View file

@ -1,6 +1,5 @@
from typing import Optional, Union
from ..rproc import boolean, nil, option_integer, option_string, option_string_bool
from ..sess import eval_lua_method_factory
@ -23,44 +22,44 @@ __all__ = (
def isPresent(side: str) -> bool:
return boolean(method('isPresent', side))
return method('isPresent', side).take_bool()
def hasData(side: str) -> bool:
return boolean(method('hasData', side))
return method('hasData', side).take_bool()
def getMountPath(side: str) -> Optional[str]:
return option_string(method('getMountPath', side))
return method('getMountPath', side).take_option_string()
def setLabel(side: str, label: str):
return nil(method('setLabel', side, label))
return method('setLabel', side, label).take_none()
def getLabel(side: str) -> Optional[str]:
return option_string(method('getLabel', side))
return method('getLabel', side).take_option_string()
def getID(side: str) -> Optional[int]:
return option_integer(method('getID', side))
return method('getID', side).take_option_int()
def hasAudio(side: str) -> bool:
return boolean(method('hasAudio', side))
return method('hasAudio', side).take_bool()
def getAudioTitle(side: str) -> Optional[Union[bool, str]]:
return option_string_bool(method('getAudioTitle', side))
return method('getAudioTitle', side).take_option_string_bool()
def playAudio(side: str):
return nil(method('playAudio', side))
return method('playAudio', side).take_none()
def stopAudio(side: str):
return nil(method('stopAudio', side))
return method('stopAudio', side).take_none()
def eject(side: str):
return nil(method('eject', side))
return method('eject', side).take_none()

View file

@ -1,55 +1,31 @@
import builtins
from contextlib import contextmanager
from typing import Optional, List
from .base import BaseSubAPI
from ..errors import LuaException
from ..lua import lua_call, lua_args, lua_string
from ..rproc import boolean, string, integer, nil, array_string, option_string, fact_scheme_dict
from ..sess import eval_lua, eval_lua_method_factory, lua_context_object
attribute = fact_scheme_dict({
'created': integer,
'modification': integer,
'isDir': boolean,
'size': integer,
}, {})
from ..lua import lua_call
from ..sess import eval_lua_method_factory, lua_context_object
class SeekMixin:
def seek(self, whence: str = None, offset: int = None) -> int:
# whence: set, cur, end
r = self._method('seek', whence, offset)
if isinstance(r, builtins.list):
assert r[0] is False
raise LuaException(r[1])
return integer(r)
rp = self._method('seek', whence, offset)
rp.check_nil_error()
return rp.take_int()
class ReadHandle(BaseSubAPI):
def _decode(self, b):
return b.decode('utf-8')
def _read(self, name, params, val):
code = '''
local s = {}.{}({})
if s == nil then return nil end
s = s:gsub('.', function(c) return string.format('%02X', string.byte(c)) end)
return s
'''.lstrip().format(
self.get_expr_code(), name, lua_args(*params),
)
return self._decode(bytes.fromhex(val(eval_lua(code))))
class ReadMixin:
def _take(self, rp):
raise NotImplementedError
def read(self, count: int = 1) -> Optional[str]:
return self._read('read', (count, ), option_string)
return self._take(self._method('read', count))
def readLine(self) -> Optional[str]:
return self._read('readLine', (), option_string)
def readLine(self, withTrailing: bool = False) -> Optional[str]:
return self._take(self._method('readLine', withTrailing))
def readAll(self) -> str:
return self._read('readAll', (), string)
def readAll(self) -> Optional[str]:
return self._take(self._method('readAll'))
def __iter__(self):
return self
@ -61,39 +37,38 @@ return s
return line
class BinaryReadHandle(ReadHandle, SeekMixin):
def _decode(self, b):
return b
class WriteHandle(BaseSubAPI):
def _encode(self, s):
return s.encode('utf-8')
def _write(self, name, text, val):
code = '''
local s = {}
s = s:gsub('..', function(cc) return string.char(tonumber(cc, 16)) end)
return {}.{}(s)
'''.lstrip().format(
lua_string(self._encode(text).hex()),
self.get_expr_code(), name,
)
return val(eval_lua(code))
class WriteMixin:
def _put(self, t):
raise NotImplementedError
def write(self, text: str):
return nil(self._method('write', text))
def writeLine(self, text: str):
return nil(self._method('writeLine', text))
return self._method('write', self._put(text)).take_none()
def flush(self):
return nil(self._method('flush'))
return self._method('flush').take_none()
class BinaryWriteHandle(WriteHandle, SeekMixin):
def _encode(self, s):
return s
class ReadHandle(ReadMixin, BaseSubAPI):
def _take(self, rp):
return rp.take_option_unicode()
class BinaryReadHandle(ReadMixin, SeekMixin, BaseSubAPI):
def _take(self, rp):
return rp.take_option_bytes()
class WriteHandle(WriteMixin, BaseSubAPI):
def _put(self, t: str) -> bytes:
return t.encode('utf-8')
def writeLine(self, text: str):
return self.write(text + '\n')
class BinaryWriteHandle(WriteMixin, SeekMixin, BaseSubAPI):
def _put(self, b: bytes) -> bytes:
return b
method = eval_lua_method_factory('fs.')
@ -124,55 +99,55 @@ __all__ = (
def list(path: str) -> List[str]:
return array_string(method('list', path))
return method('list', path).take_list_of_strings()
def exists(path: str) -> bool:
return boolean(method('exists', path))
return method('exists', path).take_bool()
def isDir(path: str) -> bool:
return boolean(method('isDir', path))
return method('isDir', path).take_bool()
def isReadOnly(path: str) -> bool:
return boolean(method('isReadOnly', path))
return method('isReadOnly', path).take_bool()
def getDrive(path: str) -> Optional[str]:
return option_string(method('getDrive', path))
return method('getDrive', path).take_option_string()
def getSize(path: str) -> int:
return integer(method('getSize', path))
return method('getSize', path).take_int()
def getFreeSpace(path: str) -> int:
return integer(method('getFreeSpace', path))
return method('getFreeSpace', path).take_int()
def getCapacity(path: str) -> int:
return integer(method('getCapacity', path))
return method('getCapacity', path).take_int()
def makeDir(path: str):
return nil(method('makeDir', path))
return method('makeDir', path).take_none()
def move(fromPath: str, toPath: str):
return nil(method('move', fromPath, toPath))
return method('move', fromPath, toPath).take_none()
def copy(fromPath: str, toPath: str):
return nil(method('copy', fromPath, toPath))
return method('copy', fromPath, toPath).take_none()
def delete(path: str):
return nil(method('delete', path))
return method('delete', path).take_none()
def combine(basePath: str, localPath: str) -> str:
return string(method('combine', basePath, localPath))
return method('combine', basePath, localPath).take_string()
@contextmanager
@ -199,27 +174,39 @@ def open(path: str, mode: str):
def find(wildcard: str) -> List[str]:
return array_string(method('find', wildcard))
return method('find', wildcard).take_list_of_strings()
def getDir(path: str) -> str:
return string(method('getDir', path))
return method('getDir', path).take_string()
def getName(path: str) -> str:
return string(method('getName', path))
return method('getName', path).take_string()
def isDriveRoot(path: str) -> bool:
return boolean(method('isDriveRoot', path))
return method('isDriveRoot', path).take_bool()
def complete(
partialName: str, path: str, includeFiles: bool = None, includeDirs: bool = None,
) -> List[str]:
return array_string(method(
'complete', partialName, path, includeFiles, includeDirs))
return method(
'complete', partialName, path, includeFiles, includeDirs,
).take_list_of_strings()
def attributes(path: str) -> dict:
return attribute(method('attributes', path))
tp = method('attributes', path).take_dict((
b'created',
b'modification',
b'isDir',
b'size',
))
r = {}
r['created'] = tp.take_int()
r['modification'] = tp.take_int()
r['isDir'] = tp.take_bool()
r['size'] = tp.take_int()
return r

View file

@ -19,4 +19,7 @@ CHANNEL_GPS = 65534
def locate(timeout: LuaNum = None, debug: bool = None) -> Optional[Tuple[LuaNum, LuaNum, LuaNum]]:
return option_tuple3_number(method('locate', timeout, debug))
rp = method('locate', timeout, debug)
if rp.peek() is None:
return None
return tuple(rp.take_number() for _ in range(3))

View file

@ -1,6 +1,5 @@
from typing import Optional, List
from ..rproc import string, nil, array_string, option_string
from ..sess import eval_lua_method_factory
@ -17,20 +16,20 @@ __all__ = (
def path() -> str:
return string(method('path'))
return method('path').take_string()
def setPath(path: str):
return nil(method('setPath', path))
return method('setPath', path).take_none()
def lookup(topic: str) -> Optional[str]:
return option_string(method('lookup', topic))
return method('lookup', topic).take_option_string()
def topics() -> List[str]:
return array_string(method('topics'))
return method('topics').take_list_of_strings()
def completeTopic(topicPrefix: str) -> List[str]:
return array_string(method('completeTopic', topicPrefix))
return method('completeTopic', topicPrefix).take_list_of_strings()

View file

@ -1,7 +1,6 @@
from typing import Optional
from ..lua import lua_string
from ..rproc import option_integer, option_string
from ..sess import eval_lua, eval_lua_method_factory
@ -17,12 +16,12 @@ __all__ = (
def getCode(name: str) -> Optional[int]:
# replaces properties
# keys.space → keys.getCode('space')
return option_integer(eval_lua('''
return eval_lua('''
if type(keys[{key}]) == 'number' then
return keys[{key}]
end
return nil'''.format(key=lua_string(name))))
return nil'''.format(key=lua_string(name))).take_option_int()
def getName(code: int) -> Optional[str]:
return option_string(method('getName', code))
return method('getName', code).take_option_string()

View file

@ -1,60 +1,62 @@
from typing import Tuple
from ..lua import LuaExpr
from ..rproc import boolean, nil, integer, tuple3_number, tuple2_integer
class TermMixin:
def write(self, text: str):
return nil(self._method('write', text))
return self._method('write', text).take_none()
def blit(self, text: str, textColors: str, backgroundColors: str):
return nil(self._method('blit', text, textColors, backgroundColors))
return self._method('blit', text, textColors, backgroundColors).take_none()
def clear(self):
return nil(self._method('clear'))
return self._method('clear').take_none()
def clearLine(self):
return nil(self._method('clearLine'))
return self._method('clearLine').take_none()
def getCursorPos(self) -> Tuple[int, int]:
return tuple2_integer(self._method('getCursorPos'))
rp = self._method('getCursorPos')
return tuple(rp.take_int() for _ in range(2))
def setCursorPos(self, x: int, y: int):
return nil(self._method('setCursorPos', x, y))
return self._method('setCursorPos', x, y).take_none()
def getCursorBlink(self) -> bool:
return boolean(self._method('getCursorBlink'))
return self._method('getCursorBlink').take_bool()
def setCursorBlink(self, value: bool):
return nil(self._method('setCursorBlink', value))
return self._method('setCursorBlink', value).take_none()
def isColor(self) -> bool:
return boolean(self._method('isColor'))
return self._method('isColor').take_bool()
def getSize(self) -> Tuple[int, int]:
return tuple2_integer(self._method('getSize'))
rp = self._method('getSize')
return tuple(rp.take_int() for _ in range(2))
def scroll(self, lines: int):
return nil(self._method('scroll', lines))
return self._method('scroll', lines).take_none()
def setTextColor(self, colorID: int):
return nil(self._method('setTextColor', colorID))
return self._method('setTextColor', colorID).take_none()
def getTextColor(self) -> int:
return integer(self._method('getTextColor'))
return self._method('getTextColor').take_int()
def setBackgroundColor(self, colorID: int):
return nil(self._method('setBackgroundColor', colorID))
return self._method('setBackgroundColor', colorID).take_none()
def getBackgroundColor(self) -> int:
return integer(self._method('getBackgroundColor'))
return self._method('getBackgroundColor').take_int()
def getPaletteColor(self, colorID: int) -> Tuple[float, float, float]:
return tuple3_number(self._method('getPaletteColor', colorID))
rp = self._method('getPaletteColor', colorID)
return tuple(rp.take_number() for _ in range(3))
def setPaletteColor(self, colorID: int, r: float, g: float, b: float):
return nil(self._method('setPaletteColor', colorID, r, g, b))
return self._method('setPaletteColor', colorID, r, g, b).take_none()
class TermTarget(LuaExpr):

View file

@ -1,6 +1,5 @@
from typing import Optional
from ..rproc import integer, nil, boolean, option_string
from ..sess import eval_lua_method_factory
@ -19,28 +18,28 @@ __all__ = (
def getCurrent() -> int:
return integer(method('getCurrent'))
return method('getCurrent').take_int()
def getCount() -> int:
return integer(method('getCount'))
return method('getCount').take_int()
def launch(environment: dict, programPath: str, *args: str) -> int:
return integer(method('launch', environment, programPath, *args))
return method('launch', environment, programPath, *args).take_int()
def setTitle(tabID: int, title: str):
return nil(method('setTitle', tabID, title))
return method('setTitle', tabID, title).take_none()
def getTitle(tabID: int) -> Optional[str]:
return option_string(method('getTitle', tabID))
return method('getTitle', tabID).take_option_string()
def setFocus(tabID: int) -> bool:
return boolean(method('setFocus', tabID))
return method('setFocus', tabID).take_bool()
def getFocus() -> int:
return integer(method('getFocus'))
return method('getFocus').take_int()

View file

@ -1,7 +1,6 @@
from typing import Optional, List
from ..lua import LuaNum
from ..rproc import nil, string, option_string, number, integer, boolean
from ..sess import eval_lua_method_factory, get_current_greenlet
@ -31,23 +30,23 @@ __all__ = (
def version() -> str:
return string(method('version'))
return method('version').take_string()
def getComputerID() -> int:
return integer(method('getComputerID'))
return method('getComputerID').take_int()
def getComputerLabel() -> Optional[str]:
return option_string(method('getComputerLabel'))
return method('getComputerLabel').take_option_string()
def setComputerLabel(label: Optional[str]):
return nil(method('setComputerLabel', label))
return method('setComputerLabel', label).take_none()
def run(environment: dict, programPath: str, *args: List[str]):
return boolean(method('run', environment, programPath, *args))
return method('run', environment, programPath, *args).take_bool()
def captureEvent(event: str):
@ -68,12 +67,12 @@ def captureEvent(event: str):
def queueEvent(event: str, *params):
return nil(method('queueEvent', event, *params))
return method('queueEvent', event, *params).take_none()
def clock() -> LuaNum:
# number of game ticks * 0.05, roughly seconds
return number(method('clock'))
return method('clock').take_number()
# regarding ingame parameter below:
@ -82,42 +81,42 @@ def clock() -> LuaNum:
def time() -> LuaNum:
# in hours 0..24
return number(method('time', 'ingame'))
return method('time', 'ingame').take_number()
def day() -> int:
return integer(method('day', 'ingame'))
return method('day', 'ingame').take_int()
def epoch() -> int:
return integer(method('epoch', 'ingame'))
return method('epoch', 'ingame').take_int()
def sleep(seconds: LuaNum):
return nil(method('sleep', seconds))
return method('sleep', seconds).take_none()
def startTimer(timeout: LuaNum) -> int:
return integer(method('startTimer', timeout))
return method('startTimer', timeout).take_int()
def cancelTimer(timerID: int):
return nil(method('cancelTimer', timerID))
return method('cancelTimer', timerID).take_none()
def setAlarm(time: LuaNum) -> int:
# takes time of the day in hours 0..24
# returns integer alarmID
return integer(method('setAlarm', time))
return method('setAlarm', time).take_int()
def cancelAlarm(alarmID: int):
return nil(method('cancelAlarm', alarmID))
return method('cancelAlarm', alarmID).take_none()
def shutdown():
return nil(method('shutdown'))
return method('shutdown').take_none()
def reboot():
return nil(method('reboot'))
return method('reboot').take_none()

View file

@ -1,10 +1,8 @@
from typing import List
from ..rproc import nil, integer, fact_array
from ..sess import eval_lua_method_factory
array_2d_integer = fact_array(fact_array(integer))
method = eval_lua_method_factory('paintutils.')
@ -20,28 +18,28 @@ __all__ = (
def parseImage(data: str) -> List[List[int]]:
return array_2d_integer(method('parseImage', data))
return method('parseImage', data).take_2d_int()
def loadImage(path: str) -> List[List[int]]:
return array_2d_integer(method('loadImage', path))
return method('loadImage', path).take_2d_int()
def drawPixel(x: int, y: int, color: int = None):
return nil(method('drawPixel', x, y, color))
return method('drawPixel', x, y, color).take_none()
def drawLine(startX: int, startY: int, endX: int, endY: int, color: int = None):
return nil(method('drawLine', startX, startY, endX, endY, color))
return method('drawLine', startX, startY, endX, endY, color).take_none()
def drawBox(startX: int, startY: int, endX: int, endY: int, color: int = None):
return nil(method('drawBox', startX, startY, endX, endY, color))
return method('drawBox', startX, startY, endX, endY, color).take_none()
def drawFilledBox(startX: int, startY: int, endX: int, endY: int, color: int = None):
return nil(method('drawFilledBox', startX, startY, endX, endY, color))
return method('drawFilledBox', startX, startY, endX, endY, color).take_none()
def drawImage(image: List[List[int]], xPos: int, yPos: int):
return nil(method('drawImage', image, xPos, yPos))
return method('drawImage', image, xPos, yPos).take_none()

View file

@ -2,12 +2,7 @@ from dataclasses import dataclass
from typing import Optional, List, Tuple, Any, Union
from .mixins import TermMixin, TermTarget
from .turtle import craft_result
from ..lua import LuaNum, lua_args, return_lua_call
from ..rproc import (
boolean, nil, integer, string, option_integer, option_string,
tuple2_integer, array_string, option_string_bool, flat_try_result,
)
from ..sess import eval_lua, eval_lua_method_factory
@ -28,65 +23,65 @@ class BasePeripheral:
class CCDrive(BasePeripheral):
def isDiskPresent(self) -> bool:
return boolean(self._method('isDiskPresent'))
return self._method('isDiskPresent').take_bool()
def getDiskLabel(self) -> Optional[str]:
return option_string(self._method('getDiskLabel'))
return self._method('getDiskLabel').take_option_string()
def setDiskLabel(self, label: str):
return nil(self._method('setDiskLabel', label))
return self._method('setDiskLabel', label).take_none()
def hasData(self) -> bool:
return boolean(self._method('hasData'))
return self._method('hasData').take_bool()
def getMountPath(self) -> Optional[str]:
return option_string(self._method('getMountPath'))
return self._method('getMountPath').take_option_string()
def hasAudio(self) -> bool:
return boolean(self._method('hasAudio'))
return self._method('hasAudio').take_bool()
def getAudioTitle(self) -> Optional[Union[bool, str]]:
return option_string_bool(self._method('getAudioTitle'))
return self._method('getAudioTitle').take_option_string_bool()
def playAudio(self):
return nil(self._method('playAudio'))
return self._method('playAudio').take_none()
def stopAudio(self):
return nil(self._method('stopAudio'))
return self._method('stopAudio').take_none()
def ejectDisk(self):
return nil(self._method('ejectDisk'))
return self._method('ejectDisk').take_none()
def getDiskID(self) -> Optional[int]:
return option_integer(self._method('getDiskID'))
return self._method('getDiskID').take_option_int()
class CCMonitor(BasePeripheral, TermMixin):
def getTextScale(self) -> int:
return integer(self._method('getTextScale'))
return self._method('getTextScale').take_int()
def setTextScale(self, scale: int):
return nil(self._method('setTextScale', scale))
return self._method('setTextScale', scale).take_none()
class ComputerMixin:
def turnOn(self):
return nil(self._method('turnOn'))
return self._method('turnOn').take_none()
def shutdown(self):
return nil(self._method('shutdown'))
return self._method('shutdown').take_none()
def reboot(self):
return nil(self._method('reboot'))
return self._method('reboot').take_none()
def getID(self) -> int:
return integer(self._method('getID'))
return self._method('getID').take_int()
def getLabel(self) -> Optional[str]:
return option_string(self._method('getLabel'))
return self._method('getLabel').take_option_string()
def isOn(self) -> bool:
return boolean(self._method('isOn'))
return self._method('isOn').take_bool()
class CCComputer(BasePeripheral, ComputerMixin):
@ -106,22 +101,22 @@ class ModemMessage:
class ModemMixin:
def isOpen(self, channel: int) -> bool:
return boolean(self._method('isOpen', channel))
return self._method('isOpen', channel).take_bool()
def open(self, channel: int):
return nil(self._method('open', channel))
return self._method('open', channel).take_none()
def close(self, channel: int):
return nil(self._method('close', channel))
return self._method('close', channel).take_none()
def closeAll(self):
return nil(self._method('closeAll'))
return self._method('closeAll').take_none()
def transmit(self, channel: int, replyChannel: int, message: Any):
return nil(self._method('transmit', channel, replyChannel, message))
return self._method('transmit', channel, replyChannel, message).take_none()
def isWireless(self) -> bool:
return boolean(self._method('isWireless'))
return self._method('isWireless').take_bool()
@property
def _side(self):
@ -151,16 +146,16 @@ class CCWirelessModem(BasePeripheral, ModemMixin):
class CCWiredModem(BasePeripheral, ModemMixin):
def getNameLocal(self) -> Optional[str]:
return option_string(self._method('getNameLocal'))
return self._method('getNameLocal').take_option_string()
def getNamesRemote(self) -> List[str]:
return array_string(self._method('getNamesRemote'))
return self._method('getNamesRemote').take_list_of_strings()
def getTypeRemote(self, peripheralName: str) -> Optional[str]:
return option_string(self._method('getTypeRemote', peripheralName))
return self._method('getTypeRemote', peripheralName).take_option_string()
def isPresentRemote(self, peripheralName: str) -> bool:
return boolean(self._method('isPresentRemote', peripheralName))
return self._method('isPresentRemote', peripheralName).take_bool()
def wrapRemote(self, peripheralName: str) -> Optional[BasePeripheral]:
# use instead getMethodsRemote and callRemote
@ -180,31 +175,33 @@ class CCWiredModem(BasePeripheral, ModemMixin):
class CCPrinter(BasePeripheral):
def newPage(self) -> bool:
return boolean(self._method('newPage'))
return self._method('newPage').take_bool()
def endPage(self) -> bool:
return boolean(self._method('endPage'))
return self._method('endPage').take_bool()
def write(self, text: str):
return nil(self._method('write', text))
return self._method('write', text).take_none()
def setCursorPos(self, x: int, y: int):
return nil(self._method('setCursorPos', x, y))
return self._method('setCursorPos', x, y).take_none()
def getCursorPos(self) -> Tuple[int, int]:
return tuple2_integer(self._method('getCursorPos'))
rp = self._method('getCursorPos')
return tuple(rp.take_int() for _ in range(2))
def getPageSize(self) -> Tuple[int, int]:
return tuple2_integer(self._method('getPageSize'))
rp = self._method('getPageSize')
return tuple(rp.take_int() for _ in range(2))
def setPageTitle(self, title: str):
return nil(self._method('setPageTitle', title))
return self._method('setPageTitle', title).take_none()
def getPaperLevel(self) -> int:
return integer(self._method('getPaperLevel'))
return self._method('getPaperLevel').take_int()
def getInkLevel(self) -> int:
return integer(self._method('getInkLevel'))
return self._method('getInkLevel').take_int()
class CCSpeaker(BasePeripheral):
@ -229,28 +226,28 @@ class CCSpeaker(BasePeripheral):
# volume 0..3
# pitch 0..24
return boolean(self._method('playNote', instrument, volume, pitch))
return self._method('playNote', instrument, volume, pitch).take_bool()
def playSound(self, sound: str, volume: int = 1, pitch: int = 1):
def playSound(self, sound: str, volume: int = 1, pitch: int = 1) -> bool:
# volume 0..3
# pitch 0..2
return boolean(self._method('playSound', sound, volume, pitch))
return self._method('playSound', sound, volume, pitch).take_bool()
class CCCommandBlock(BasePeripheral):
def getCommand(self) -> str:
return string(self._method('getCommand'))
return self._method('getCommand').take_string()
def setCommand(self, command: str):
return nil(self._method('setCommand', command))
return self._method('setCommand', command).take_none()
def runCommand(self):
return flat_try_result(self._method('runCommand'))
return self._method('runCommand').check_bool_error()
class CCWorkbench(BasePeripheral):
def craft(self, quantity: int = 64) -> bool:
return craft_result(self._method('craft', quantity))
return self._method('craft', quantity).bool_error_exclude('No matching recipes')
TYPE_MAP = {
@ -278,15 +275,15 @@ __all__ = (
def isPresent(side: str) -> bool:
return boolean(method('isPresent', side))
return method('isPresent', side).take_bool()
def getType(side: str) -> Optional[str]:
return option_string(method('getType', side))
return method('getType', side).take_option_string()
def getNames() -> List[str]:
return array_string(method('getNames'))
return method('getNames').take_list_of_strings()
# use instead getMethods and call
@ -298,7 +295,7 @@ def wrap(side: str) -> Optional[BasePeripheral]:
m = 'peripheral.call'
if ptype == 'modem':
if boolean(method('call', side, 'isWireless')):
if method('call', side, 'isWireless').take_bool():
return CCWirelessModem(m, side)
else:
return CCWiredModem(m, side)

View file

@ -1,4 +1,3 @@
from ..rproc import flat_try_result
from ..sess import eval_lua_method_factory
@ -12,8 +11,8 @@ __all__ = (
def equipBack():
return flat_try_result(method('equipBack'))
return method('equipBack').check_bool_error()
def unequipBack():
return flat_try_result(method('unequipBack'))
return method('unequipBack').check_bool_error()

View file

@ -1,16 +1,9 @@
from typing import Any, List, Optional, Tuple, Union
from ..lua import LuaNum
from ..rproc import nil, integer, option_string, boolean, array_integer, option_integer, fact_option, fact_tuple
from ..sess import eval_lua_method_factory
recv_result = fact_option(fact_tuple(
integer,
lambda v: v,
option_string,
tail_nils=1,
))
method = eval_lua_method_factory('rednet.')
@ -34,46 +27,47 @@ CHANNEL_BROADCAST = 65535
def open(side: str):
return nil(method('open', side))
return method('open', side).take_none()
def close(side: str = None):
return nil(method('close', side))
return method('close', side).take_none()
def send(receiverID: int, message: Any, protocol: str = None) -> bool:
return boolean(method('send', receiverID, message, protocol))
return method('send', receiverID, message, protocol).take_bool()
def broadcast(message: Any, protocol: str = None):
return nil(method('broadcast', message, protocol))
return method('broadcast', message, protocol).take_none()
def receive(
protocolFilter: str = None, timeout: LuaNum = None,
) -> Optional[Tuple[int, Any, Optional[str]]]:
return recv_result(method('receive', protocolFilter, timeout))
rp = method('receive', protocolFilter, timeout)
if rp.peek() is None:
return None
return (rp.take_int(), rp.take(), rp.take_option_string())
def isOpen(side: str = None) -> bool:
return boolean(method('isOpen', side))
return method('isOpen', side).take_bool()
def host(protocol: str, hostname: str):
return nil(method('host', protocol, hostname))
return method('host', protocol, hostname).take_none()
def unhost(protocol: str):
return nil(method('unhost', protocol))
return method('unhost', protocol).take_none()
def lookup(protocol: str, hostname: str = None) -> Union[Optional[int], List[int]]:
result = method('lookup', protocol, hostname)
rp = method('lookup', protocol, hostname)
if hostname is None:
if result is None:
if rp.peek() is None:
return []
if isinstance(result, list):
return array_integer(result)
return [integer(result)]
return rp.take_list_of_ints()
else:
return option_integer(result)
return rp.take_option_int()

View file

@ -1,6 +1,5 @@
from typing import List
from ..rproc import boolean, nil, integer, array_string
from ..sess import eval_lua_method_factory
@ -23,46 +22,46 @@ __all__ = (
def getSides() -> List[str]:
return array_string(method('getSides'))
return method('getSides').take_list_of_strings()
def getInput(side: str) -> bool:
return boolean(method('getInput', side))
return method('getInput', side).take_bool()
def setOutput(side: str, value: bool):
return nil(method('setOutput', side, value))
return method('setOutput', side, value).take_none()
def getOutput(side: str) -> bool:
return boolean(method('getOutput', side))
return method('getOutput', side).take_bool()
def getAnalogInput(side: str) -> int:
return integer(method('getAnalogInput', side))
return method('getAnalogInput', side).take_int()
def setAnalogOutput(side: str, strength: int):
return nil(method('setAnalogOutput', side, strength))
return method('setAnalogOutput', side, strength).take_none()
def getAnalogOutput(side: str) -> int:
return integer(method('getAnalogOutput', side))
return method('getAnalogOutput', side).take_int()
# bundled cables are not available in vanilla
def getBundledInput(side: str) -> int:
return integer(method('getBundledInput', side))
return method('getBundledInput', side).take_int()
def setBundledOutput(side: str, colors: int):
return nil(method('setBundledOutput', side, colors))
return method('setBundledOutput', side, colors).take_none()
def getBundledOutput(side: str) -> int:
return integer(method('getBundledOutput', side))
return method('getBundledOutput', side).take_int()
def testBundledInput(side: str, color: int) -> bool:
return boolean(method('testBundledInput', side, color))
return method('testBundledInput', side, color).take_bool()

View file

@ -1,17 +1,8 @@
from typing import Any, List
from ..rproc import nil, boolean, string, array_string, fact_scheme_dict
from ..sess import eval_lua_method_factory
setting = fact_scheme_dict({
'changed': boolean,
}, {
'description': string,
'default': lambda v: v,
'type': string,
'value': lambda v: v,
})
method = eval_lua_method_factory('settings.')
@ -37,40 +28,57 @@ def define(name: str, description: str = None, default: Any = None, type: str =
options['default'] = default
if type is not None:
options['type'] = type
return nil(method('define', name, options))
return method('define', name, options).take_none()
def undefine(name: str):
return nil(method('undefine', name))
return method('undefine', name).take_none()
def getDetails(name: str) -> dict:
return setting(method('getDetails', name))
tp = method('getDetails', name).take_dict((
b'changed',
b'description',
b'default',
b'type',
b'value',
))
r = {}
r['changed'] = tp.take_bool()
for k, v in [
('description', tp.take_option_string()),
('default', tp.take()),
('type', tp.take_option_string()),
('value', tp.take()),
]:
if v is not None:
r[k] = v
return r
def set(name: str, value: Any):
return nil(method('set', name, value))
return method('set', name, value).take_none()
def get(name: str, default: Any = None) -> Any:
return method('get', name, default)
return method('get', name, default).take()
def unset(name: str):
return nil(method('unset', name))
return method('unset', name).take_none()
def clear():
return nil(method('clear'))
return method('clear').take_none()
def getNames() -> List[str]:
return array_string(method('getNames'))
return method('getNames').take_list_of_strings()
def load(path: str = None) -> bool:
return boolean(method('load', path))
return method('load', path).take_bool()
def save(path: str = None) -> bool:
return boolean(method('save', path))
return method('save', path).take_bool()

View file

@ -1,10 +1,8 @@
from typing import List, Dict, Optional
from ..rproc import nil, string, boolean, integer, array_string, fact_mono_dict, option_string
from ..sess import eval_lua_method_factory
map_string_string = fact_mono_dict(string, string)
method = eval_lua_method_factory('shell.')
@ -31,75 +29,76 @@ __all__ = (
def exit():
return nil(method('exit'))
return method('exit').take_none()
def dir() -> str:
return string(method('dir'))
return method('dir').take_string()
def setDir(path: str):
return nil(method('setDir', path))
return method('setDir', path).take_none()
def path() -> str:
return string(method('path'))
return method('path').take_string()
def setPath(path: str):
return nil(method('setPath', path))
return method('setPath', path).take_none()
def resolve(localPath: str) -> str:
return string(method('resolve', localPath))
return method('resolve', localPath).take_string()
def resolveProgram(name: str) -> Optional[str]:
return option_string(method('resolveProgram', name))
return method('resolveProgram', name).take_option_string()
def aliases() -> Dict[str, str]:
return map_string_string(method('aliases'))
d = method('aliases').take_dict()
return {k.decode('latin1'): v.decode('latin1') for k, v in d.items()}
def setAlias(alias: str, program: str):
return nil(method('setAlias', alias, program))
return method('setAlias', alias, program).take_none()
def clearAlias(alias: str):
return nil(method('clearAlias', alias))
return method('clearAlias', alias).take_none()
def programs(showHidden: bool = None) -> List[str]:
return array_string(method('programs', showHidden))
return method('programs', showHidden).take_list_of_strings()
def getRunningProgram() -> str:
return string(method('getRunningProgram'))
return method('getRunningProgram').take_string()
def run(command: str, *args: str) -> bool:
return boolean(method('run', command, *args))
return method('run', command, *args).take_bool()
def execute(command: str, *args: str) -> bool:
return boolean(method('execute', command, *args))
return method('execute', command, *args).take_bool()
def openTab(command: str, *args: str) -> int:
return integer(method('openTab', command, *args))
return method('openTab', command, *args).take_int()
def switchTab(tabID: int):
return nil(method('switchTab', tabID))
return method('switchTab', tabID).take_none()
def complete(prefix: str) -> List[str]:
return array_string(method('complete', prefix))
return method('complete', prefix).take_list_of_strings()
def completeProgram(prefix: str) -> List[str]:
return array_string(method('completeProgram', prefix))
return method('completeProgram', prefix).take_list_of_strings()
# TODO: ?
# these functions won't be implemented

View file

@ -4,7 +4,6 @@ from typing import Tuple
from .base import BaseSubAPI
from .mixins import TermMixin, TermTarget
from ..lua import lua_call
from ..rproc import tuple3_number
from ..sess import eval_lua_method_factory, lua_context_object
@ -61,7 +60,8 @@ setPaletteColor = tapi.setPaletteColor
def nativePaletteColor(colorID: int) -> Tuple[float, float, float]:
return tuple3_number(method('nativePaletteColor', colorID))
rp = method('nativePaletteColor', colorID)
return tuple(rp.take_number() for _ in range(3))
@contextmanager

View file

@ -1,7 +1,6 @@
from typing import List, Union
from ..lua import LuaNum
from ..rproc import nil, string, integer
from ..sess import eval_lua_method_factory
@ -20,27 +19,27 @@ __all__ = (
def slowWrite(text: str, rate: LuaNum = None):
return nil(method('slowWrite', text, rate))
return method('slowWrite', text, rate).take_none()
def slowPrint(text: str, rate: LuaNum = None):
return nil(method('slowPrint', text, rate))
return method('slowPrint', text, rate).take_none()
def formatTime(time: LuaNum, twentyFourHour: bool = None) -> str:
return string(method('formatTime', time, twentyFourHour))
return method('formatTime', time, twentyFourHour).take_string()
def tabulate(*rows_and_colors: Union[list, int]):
return nil(method('tabulate', *rows_and_colors))
return method('tabulate', *rows_and_colors).take_none()
def pagedTabulate(*rows_and_colors: Union[list, int]):
return nil(method('pagedTabulate', *rows_and_colors))
return method('pagedTabulate', *rows_and_colors).take_none()
def pagedPrint(text: str, freeLines: int = None) -> int:
return integer(method('pagedPrint', text, freeLines))
return method('pagedPrint', text, freeLines).take_int()
def complete(partial: str, possible: List[str]) -> List[str]:

View file

@ -1,41 +1,26 @@
from typing import Optional
from ..errors import LuaException
from ..rproc import integer, boolean, fact_option, any_dict, flat_try_result
from ..sess import eval_lua_method_factory
method = eval_lua_method_factory('turtle.')
option_any_dict = fact_option(any_dict)
def inspect_result(r):
assert isinstance(r, list)
assert len(r) == 2
success, data = r
assert isinstance(success, bool)
def inspect_result(rp):
success = rp.take_bool()
if not success:
if data == 'No block to inspect':
msg = rp.take_string()
if msg == 'No block to inspect':
return None
raise LuaException(data)
raise LuaException(msg)
else:
return any_dict(data)
return rp.take_dict()
def boolean_with_error_exclusion(exclude_msg):
def proc(r):
if r is True:
return True
assert isinstance(r, list)
assert len(r) == 2
success, msg = r
assert isinstance(success, bool)
if not success:
if msg == exclude_msg:
return False
raise LuaException(msg)
else:
return True
def proc(rp):
return rp.bool_error_exclude(exclude_msg)
return proc
@ -49,8 +34,8 @@ attack_result = boolean_with_error_exclusion('Nothing to attack here')
craft_result = boolean_with_error_exclusion('No matching recipes')
def always_true(r):
assert boolean(r) is True
def always_true(rp):
assert rp.take_bool() is True
# return value is useless
return None
@ -135,19 +120,22 @@ def select(slotNum: int):
def getSelectedSlot() -> int:
return integer(method('getSelectedSlot'))
return method('getSelectedSlot').take_int()
def getItemCount(slotNum: int = None) -> int:
return integer(method('getItemCount', slotNum))
return method('getItemCount', slotNum).take_int()
def getItemSpace(slotNum: int = None) -> int:
return integer(method('getItemSpace', slotNum))
return method('getItemSpace', slotNum).take_int()
def getItemDetail(slotNum: int = None) -> dict:
return option_any_dict(method('getItemDetail', slotNum))
def getItemDetail(slotNum: int = None) -> Optional[dict]:
rp = method('getItemDetail', slotNum)
if rp.peek() is None:
return None
return rp.take_dict()
def equipLeft():
@ -195,15 +183,15 @@ def placeDown() -> bool:
def detect() -> bool:
return boolean(method('detect'))
return method('detect').take_bool()
def detectUp() -> bool:
return boolean(method('detectUp'))
return method('detectUp').take_bool()
def detectDown() -> bool:
return boolean(method('detectDown'))
return method('detectDown').take_bool()
def inspect() -> Optional[dict]:
@ -219,19 +207,19 @@ def inspectDown() -> Optional[dict]:
def compare() -> bool:
return boolean(method('compare'))
return method('compare').take_bool()
def compareUp() -> bool:
return boolean(method('compareUp'))
return method('compareUp').take_bool()
def compareDown() -> bool:
return boolean(method('compareDown'))
return method('compareDown').take_bool()
def compareTo(slot: int) -> bool:
return boolean(method('compareTo', slot))
return method('compareTo', slot).take_bool()
def drop(count: int = None) -> bool:
@ -259,15 +247,15 @@ def suckDown(amount: int = None) -> bool:
def refuel(quantity: int = None):
return flat_try_result(method('refuel', quantity))
return method('refuel', quantity).check_bool_error()
def getFuelLevel() -> int:
return integer(method('getFuelLevel'))
return method('getFuelLevel').take_int()
def getFuelLimit() -> int:
return integer(method('getFuelLimit'))
return method('getFuelLimit').take_int()
def transferTo(slot: int, quantity: int = None) -> bool:

View file

@ -2,7 +2,6 @@ from contextlib import contextmanager
from typing import Tuple
from ..lua import lua_call
from ..rproc import nil, tuple2_integer, tuple3_string
from ..sess import eval_lua_method_factory, lua_context_object
from .base import BaseSubAPI
from .mixins import TermMixin, TermTarget
@ -10,22 +9,24 @@ from .mixins import TermMixin, TermTarget
class CCWindow(BaseSubAPI, TermMixin):
def setVisible(self, visibility: bool):
return nil(self._method('setVisible', visibility))
return self._method('setVisible', visibility).take_none()
def redraw(self):
return nil(self._method('redraw'))
return self._method('redraw').take_none()
def restoreCursor(self):
return nil(self._method('restoreCursor'))
return self._method('restoreCursor').take_none()
def getPosition(self) -> Tuple[int, int]:
return tuple2_integer(self._method('getPosition'))
rp = self._method('getPosition')
return tuple(rp.take_int() for _ in range(2))
def reposition(self, x: int, y: int, width: int = None, height: int = None, parent: TermTarget = None):
return nil(self._method('reposition', x, y, width, height, parent))
return self._method('reposition', x, y, width, height, parent).take_none()
def getLine(self, y: int) -> Tuple[str, str, str]:
return tuple3_string(self._method('getLine', y))
rp = self._method('getLine', y)
return tuple(rp.take_string() for _ in range(3))
def get_term_target(self) -> TermTarget:
return TermTarget(self.get_expr_code())

View file

@ -39,7 +39,7 @@ def step(text):
def get_object_table(objname):
r = eval_lua(f"""
rp = eval_lua(f"""
local r = {{}}
for k in pairs({objname}) do
local t = type({objname}[k])
@ -51,8 +51,12 @@ for k in pairs({objname}) do
end
end
return r""", immediate=True)
assert len(r) == 1
return r[0]
d = rp.take_dict()
return {
k1.decode('latin1'): {
k2.decode('latin1'): v for k2, v in t.items()
} for k1, t in d.items()
}
def get_class_table(cls):

View file

@ -151,50 +151,32 @@ assert fs.complete('ap', 'tdir', includeFiles=False) == []
assert fs.getSize('tdir/banana') == 9
with fs.open('tdir/banana', 'r') as f:
assert _lib.get_object_table(f.get_expr_code()) == {'function': {
'close': True,
'read': True,
'readLine': True,
'readAll': True,
}}
assert f.read(4) == 'text'
assert f.readLine() == 'line'
assert f.read(1) is None
assert f.readLine() is None
assert f.readAll() == ''
assert f.readAll() == ''
assert f.readAll() is None
assert f.readAll() is None
assert fs.getSize('tdir/banana') == 9
with fs.open('tdir/banana', 'a') as f:
assert _lib.get_object_table(f.get_expr_code()) == {'function': {
'close': True,
'write': True,
'writeLine': True,
'flush': True,
}}
assert f.write('x') is None
assert fs.getSize('tdir/banana') == 10
with fs.open('tdir/banana', 'w') as f:
pass
assert fs.getSize('tdir/banana') == 0 # truncate
with fs.open('tdir/banana', 'w') as f:
assert _lib.get_object_table(f.get_expr_code()) == {'function': {
'close': True,
'write': True,
'writeLine': True,
'flush': True,
}}
assert f.write('Bro') is None
assert f.writeLine('wn fox jumps') is None
assert fs.getSize('tdir/banana') == 0 # changes are not on a disk
# assert fs.getSize('tdir/banana') == 0 # changes are not on a disk
assert f.flush() is None
assert fs.getSize('tdir/banana') == len('Brown fox jumps\n')
assert f.write('ov') is None
assert f.write('er ') is None
assert f.write('a lazy') is None
assert f.writeLine(' dog.') is None
assert f.writeLine(' дог.') is None
assert fs.getSize('tdir/banana') > 9
with fs.open('tdir/banana', 'r') as f:
assert f.readAll() == 'Brown fox jumps\nover a lazy dog.' # no newline?
assert f.readAll() == 'Brown fox jumps\nover a lazy дог.\n'
with assert_raises(LuaException):
with fs.open('tdir/banana', 'rw') as f:
pass
@ -202,22 +184,19 @@ with assert_raises(LuaException):
assert fs.exists('tdir/banana') is True
with fs.open('tdir/binfile', 'wb') as f:
assert f.write('a' * 9) is None
assert f.write(b'a' * 9) is None
assert f.seek() == 9
assert f.seek('set', 0) == 0
assert f.write('b' * 3) is None
assert f.write(b'b' * 3) is None
assert f.seek('cur', -1) == 2
assert f.write('c' * 3) is None
assert f.write(b'c' * 3) is None
assert f.seek('end') == 9
assert f.write('d' * 3) is None
assert f.write(b'd' * 3) is None
with assert_raises(LuaException):
f.seek('set', -10)
with fs.open('tdir/binfile', 'rb') as f:
assert f.readAll() == 'bbcccaaaaddd'
with fs.open('tdir/binfile', 'rb') as f:
assert isinstance(f.read(), int)
assert f.readAll() == b'bbcccaaaaddd'
with fs.open('tdir/binfile', 'r') as f:
assert [line for line in f] == ['bbcccaaaaddd']

View file

@ -146,3 +146,5 @@ for _, v in ipairs(roundtrip_tables) do
end
print('ALL OK')
print(serialize({true, false, 'Position is negative'}))