aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Touhey <thomas@touhey.fr>2021-06-01 13:15:46 +0200
committerThomas Touhey <thomas@touhey.fr>2021-06-01 13:15:46 +0200
commit41a947fa67e111c71c8cfbf81da3b4adcf724363 (patch)
tree36cbea80b6224fef73071b33afcdc1bcd8f59c34
parentb143d7969fca33f660254ecc84ed4575db660f5b (diff)
A few things, still working on the base system.
-rw-r--r--docs/system.rst1
-rw-r--r--docs/system/pkgd.rst34
-rw-r--r--docs/system/pkgd/pkgbuild.rst66
-rw-r--r--pkg/core/daemon/pkgbuild.lua4
-rw-r--r--pkg/core/startup-cc/pkgbuild.lua (renamed from pkg/core/startup/pkgbuild.lua)7
-rw-r--r--pkg/core/startup-cc/startup (renamed from pkg/core/startup/startup)0
-rw-r--r--pkg/core/startup-cc/startup.lua656
-rw-r--r--pkg/core/startup/startup.lua175
-rw-r--r--pkg/core/thox/pkgbuild.lua2
-rw-r--r--pkg/core/thox/system.lua591
10 files changed, 1070 insertions, 466 deletions
diff --git a/docs/system.rst b/docs/system.rst
index d47b9f4..1042955 100644
--- a/docs/system.rst
+++ b/docs/system.rst
@@ -20,6 +20,7 @@ components.
system/gpsd
system/netd
system/rand
+ system/pkgd
.. todo::
diff --git a/docs/system/pkgd.rst b/docs/system/pkgd.rst
new file mode 100644
index 0000000..516e141
--- /dev/null
+++ b/docs/system/pkgd.rst
@@ -0,0 +1,34 @@
+.. _thox-pkgd:
+
+pkgd: the thox package management daemon
+========================================
+
+thox manages the system through the filesystem using a dedicated package
+manager, simply named pkgd for "package (management) daemon". The goal of
+such a software, to the end user, is to:
+
+ * Install features on a per-need basis, with dependencies.
+ * Keep the system up to date, thanks to a client/server protocol using
+ HTTP and static files.
+
+The choice of using a daemon instead of a library comes from the fact that
+the system and its files are a shared resource between processes, and thus
+accesses and modifications need to be regulated to avoid conflicts and
+corruptions.
+
+.. todo::
+
+ At the beginning, package management should actually not be managed by
+ a daemon, but the system should be built statically through a little
+ utility in order to have the repository managed by a non-thox system.
+ However, that prevents thox-specific tools being used, which is really
+ a shame since building thox packages on thox is the end objective;
+ maybe some kind of simulator can be done?
+
+ Maybe a pacstrap equivalent, which needs not to be used on the system
+ which interacting is made with, but another thox-based system? Yeah,
+ chicken & egg problem, I'll have to work on that.
+
+.. toctree::
+
+ pkgd/pkgbuild
diff --git a/docs/system/pkgd/pkgbuild.rst b/docs/system/pkgd/pkgbuild.rst
new file mode 100644
index 0000000..f75c3ea
--- /dev/null
+++ b/docs/system/pkgd/pkgbuild.rst
@@ -0,0 +1,66 @@
+pkgbuild format
+===============
+
+Source packages include a special Lua file that describes the package and
+how to build it, entitled ``pkgbuild.lua``. Their role and format are
+inspired by Archlinux's PKGBUILD_ files.
+
+The following properties should be defined:
+
+``pkgname``
+ The technical name of the source package; should also correspond to
+ the name of the build package.
+
+``pkgver``
+ The upstream package version, expressed as a string only containing
+ letters, numbers, dots and underscores.
+
+``pkgrel``
+ The package release number as an integer, relative to the upstream
+ package version.
+
+``pkgdesc``
+ A short and inline description of the source package.
+
+``url``
+ The URL of the official site of the upstream software being packaged.
+
+``license``
+ The identifier of the license of the software being packaged.
+ For now, only ``MIT`` is allowed.
+
+``depends``
+ A sequence representing the packages on which the current source package
+ depends at runtime.
+
+``source``
+ A sequence of the source files or archives being required to build the
+ package. For now these files must be included with the pkgbuild.
+
+The following functions should be defined:
+
+``build(pkgdir, srcdir)``
+ The build function, that takes the destination package directory
+ and the source directory in which source files are available.
+
+ .. todo::
+
+ Should archives be directly unpacked?
+
+ .. todo::
+
+ What functions are available to this function? What members?
+ The following functions should at least be available, in addition
+ to copies of the pkgbuild globals:
+
+ ::
+
+ fs.combine(path, ...) -- combine paths with separators.
+ fs.makedirs(path) -- make multiple parent directories.
+ fs.copy(dest_path, src_path) -- copy a given file.
+ fs.listdir(path) -- list files and directories.
+
+ And what about packages being required for build? Can they be
+ represented using out of system builds? (kind of cross compiling)
+
+.. _PKGBUILD: https://wiki.archlinux.org/title/PKGBUILD
diff --git a/pkg/core/daemon/pkgbuild.lua b/pkg/core/daemon/pkgbuild.lua
index 5829a1f..f43f1fe 100644
--- a/pkg/core/daemon/pkgbuild.lua
+++ b/pkg/core/daemon/pkgbuild.lua
@@ -1,5 +1,5 @@
pkgname = "daemon"
-pkgver = "2021-03-29-01"
+pkgver = "2021032901"
pkgrel = 0
pkgdesc = "thox daemon library"
url = "https://thox.madefor.cc/libs/daemon.html"
@@ -10,7 +10,7 @@ source = {
"daemon.lua"}
function build(pkgdir, srcdir)
- fs.makeDirs(fs.combine(pkgdir, "lib"))
+ fs.makedirs(fs.combine(pkgdir, "lib"))
fs.copy(fs.combine(pkgdir, "lib", "daemon.lua"),
fs.combine(srcdir, "daemon.lua"))
end
diff --git a/pkg/core/startup/pkgbuild.lua b/pkg/core/startup-cc/pkgbuild.lua
index 65b9655..bb9c093 100644
--- a/pkg/core/startup/pkgbuild.lua
+++ b/pkg/core/startup-cc/pkgbuild.lua
@@ -1,11 +1,12 @@
-pkgname = "startup"
-pkgver = "2021-03-29-01"
+pkgname = "startup-cc"
+pkgver = "2021060101"
pkgrel = 0
-pkgdesc = "thox startup"
+pkgdesc = "thox startup for CC:T computers"
url = "https://thox.madefor.cc/system/startup.html"
license = "MIT"
depends = {}
+provides = {"startup"}
source = {
"startup",
"startup.lua"}
diff --git a/pkg/core/startup/startup b/pkg/core/startup-cc/startup
index 1dcab5b..1dcab5b 100644
--- a/pkg/core/startup/startup
+++ b/pkg/core/startup-cc/startup
diff --git a/pkg/core/startup-cc/startup.lua b/pkg/core/startup-cc/startup.lua
new file mode 100644
index 0000000..6c9451d
--- /dev/null
+++ b/pkg/core/startup-cc/startup.lua
@@ -0,0 +1,656 @@
+-- a simple startup script for booting thox in a standardized environment
+-- from the startup ComputerCraft: Tweaked environment.
+--
+-- Copyright (C) 2020-2021 Thomas Touhey <thomas@touhey.fr>
+-- This file is part of the thox project, which is MIT-licensed.
+--
+-- This file contains a basic compatibility layer which reinterprets all of
+-- the basic APIs into a peripheral / component logic, for allowing more
+-- advanced OSes to take place.
+--
+-- For more information about this component, consult the documentation
+-- available at <https://thox.madefor.cc/system/startup.html>.
+--
+-- Basic variables that should be accessible at all times:
+--
+-- * `G`: the exported functions to the final system environment,
+-- set using the `__index` member of the environment's variable.
+-- * `inals`: functions and other utilities defined by the startup script
+-- for itself.
+-- * `bios`: the isolated functions available from the basic environment
+-- from which the startup script is run. These utilities are either used
+-- in the rest that this script defines, or disappear for
+-- compatibility [1].
+--
+-- [1] We do not let unsupported utilities filter out to the system in order
+-- to avoid systems using machine-specific elements, defeating the purpose
+-- of such a script.
+
+local G = {}, bios = {}, inals = {}
+
+-- Check if we are in the right environment. Otherwise, there might be a
+-- need to change startup script from a rescue environment.
+
+if string.sub(_HOST, 1, 14) ~= "ComputerCraft " then
+ print("!!! INVALID ENVIRONMENT FOR COMPUTERCRAFT STARTUP:")
+ print(" " .. _HOST)
+ print("!!! PLEASE REBOOT IN RESCUE MODE AND FIX THE STARTUP USED.")
+
+ while true do
+ sleep(60)
+ end
+end
+
+-- Start to prepare the `inals` (for "internals") table, and prepare
+-- the getupvalue function for getting a function upvalue by name.
+--
+-- getupvalue might not be defined if the `debug` module is not available,
+-- or the `debug.getupvalue` function is not defined in our current
+-- environment; this might be the case when ComputerCraft doesn't enable
+-- the debug module.
+
+inals = {
+ version = _VERSION,
+ host = _HOST,
+
+ type = type,
+ pairs = pairs,
+ ipairs = ipairs,
+ setmetatable = setmetatable
+}
+
+if type(debug) == "table" and type(debug.getupvalue) == "function" then
+ local dbg_getupvalue = debug.getupvalue
+
+ inals.getupvalue = function (func, name)
+ local i = 1
+
+ while true do
+ local e_name, e_val = dbg_getupvalue(func, i)
+ if e_name == name then
+ return e_val
+ elseif e_name == nil then
+ return nil
+ end
+ end
+ end
+end
+
+-- Reverse some things lost in the native bios (but not everything,
+-- unfortunately); for CraftOS bioses, see the following:
+-- <https://github.com/SquidDev-CC/CC-Tweaked/blob/b97e950d86e4694409c01e507db34a87da85e452/src/main/resources/data/computercraft/lua/bios.lua>
+--
+-- TODO: some Lua 5.1 utilities are definitely lost if the
+-- option is enabled in ComputerCraft and we have the default BIOS...
+-- what to do in that case?
+
+if (_VERSION == "Lua 5.1" and type(bios.os.version) == "function"
+ and inals.getupvalue ~= nil) then
+ local ver = _VERSION, osver = bios.os.version()
+
+ if osver == "CraftOS 1.8" and _CC_DISABLE_LUA51_FEATURES then
+ _G.load = inals.getupvalue(load, "nativeload")
+ _G.loadstring = inals.getupvalue(load, "nativeloadstring")
+ _G.setfenv = inals.getupvalue(load, "nativesetfenv")
+
+ -- TODO: other functions we want to hack.
+ end
+end
+
+-- Fill the internals table with other useful functions:
+--
+-- * version: the _VERSION field, copied for it to be accessible at all time.
+-- * host: same, but for the _HOST field.
+-- * pairs, setmetatable: standard functions to be available at all time
+-- during the setup.
+-- * table.insert, table.remove: standard functions to be available at
+-- all time during the setup, equivalent to table.insert and table.remove.
+-- * getupvalue: get an upvalue using a name.
+-- * print: print a message on the terminal or standard output, depending
+-- on the environment we're on, for the user to see.
+-- * panic: exit the startup script by shutting down the computer.
+--
+-- c_print is currently calibrated for ComputerCraft only. It must be
+-- undefined if ComputerCraft is not the current os we're on.
+--
+-- panic is CCEmuX-aware: if a CCEmuX host is detected, the emulator is
+-- closed instead of just shutting down the computer (which would make you
+-- obtain a simple black screen).
+
+do
+ inals.math = {}
+
+ if math.isinteger ~= nil then
+ inals.math.isinteger = math.isinteger
+ else
+ local floor = math.floor
+
+ function inals.math.isinteger(n)
+ return type(n) == "number" and floor(n) == n
+ end
+ end
+end
+
+do
+ -- Table function definitions.
+ -- We define everything we need here in order not to pollute
+ -- other code too much.
+
+ inals.table = {}
+ inals.table.insert = table.insert
+ inals.table.remove = table.remove
+
+ function inals.table.isSequence(t)
+ if type(t) ~= "table" then
+ return false
+ elseif not inals.math.isinteger(t.n) then
+ return false
+ end
+
+ return true
+ end
+
+ -- Important remark: if `t` is a sequence, it defines the `n` field
+ -- and all other fields are integers (numbers).
+
+ function inals.table.sequenceContains(t, e)
+ if type(t) ~= "table" then
+ return false
+ end
+
+ for _, value in inals.ipairs(t) do
+ if value == e then
+ return true
+ end
+ end
+
+ return false
+ end
+
+ -- This function copies a table with all keys, excluding a given set
+ -- of values represented as a sequence. This function works best with
+ -- tables without a metatable; only keys and value will be copied,
+ -- the source's metatable will not.
+
+ do
+ local function copyKeysAndValues(d, s, excludedKeys)
+ for key, value in inals.pairs(s) do
+ if inals.table.sequenceContains(excludedKeys, key) then
+ -- We do not copy such a key.
+ elseif type(value) == "table" then
+ -- We recreate the table and copy recursively.
+ -- This will cause metatables to be lost.
+
+ d[key] = {}
+ copyKeysAndValues(d[key], s[key], excludedKeys)
+ else
+ d[key] = s[key]
+ end
+ end
+ end
+
+ inals.table.copyKeysAndValues = copyKeysAndValues
+ end
+
+ -- Interact with sequences.
+
+ function inals.table.addValuesToSequence(t, es)
+ for _, value in inals.ipairs(es) do
+ inals.table.insert(t, value)
+ end
+ end
+
+ function inals.table.removeValuesFromSequence(t, es)
+ for t_key in #t - 1, 1, -1 do
+ local key = t_key
+
+ for _, name in inals.pairs(es) do
+ if key == name then
+ inals.table.remove(t, t_key)
+ break
+ end
+ end
+ end
+ end
+end
+
+do
+ local setBackgroundColor = term.setBackgroundColor
+ local setTextColor = term.setTextColor
+ local clear = term.clear
+ local setCursorPos = term.setCursorPos
+
+ local BLACK = 32768, WHITE = 1
+ local YELLOW = 16, RED = 16384
+ local last_color = 0
+
+ local function setColors(fore, back)
+ if setBackgroundColor ~= nil then
+ setBackgroundColor(back)
+ end
+ if setTextColor ~= nil then
+ setTextColor(fore)
+ end
+ end
+
+ inals.print = function (message, color)
+ if color == "red" then
+ color = RED
+ elseif color == "yellow" then
+ color = YELLOW
+ else
+ color = WHITE
+ end
+
+ if color ~= last_color then
+ setColors(color, BLACK)
+ end
+
+ print(tostring(message))
+ end
+
+ clear()
+ setCursorPos(1, 1)
+end
+
+do
+ local startTimer = os.startTimer
+ local pullEvent = os.pullEventRaw
+ local shutdown = os.shutdown
+ local print = inals.print
+
+ if type(ccemux) == "table" and type(ccemux.closeEmu) == "function" then
+ shutdown = ccemux.closeEmu
+ end
+
+ inals.panic = function ()
+ print("The computer will be shut down in 5 seconds.", "red")
+
+ local id = startTimer(5)
+ while true do
+ local e_type, e_id = pullEvent()
+ if e_type == "timer" and e_id == id then
+ break
+ end
+ end
+
+ shutdown()
+ end
+end
+
+-- Setup the environment by isolating everything that's non
+-- standard in a `bios` module, so that we can prepare our own
+-- basic APIs with serenity.
+--
+-- For this we copy what's in _G, as we've only encountered BIOSes which
+-- set up their functions in _G for now (using it as an __index on the
+-- _ENV metatable).
+
+do
+ G._ENV = _ENV
+ G._G = G
+ G._VERSION = _VERSION
+
+ inals.table.copyKeysAndValues(G, _G, {"_G", "_ENV"})
+ inals.G = G
+end
+
+-- And now, we set the internals as the __index for the current environment.
+-- From now on, any reference to `inals` should be done directly.
+
+inals.setmetatable(_ENV, {__index = inals})
+
+-- Then copy the standard functions of the corresponding version of Lua.
+-- See the reference here:
+-- <https://thox.madefor.cc/system/startup/startup-api.html#standard-apis>
+
+local supported_versions = {
+ "Lua 5.1" = true,
+ "Lua 5.2" = true,
+ "Lua 5.3" = true}
+
+if not supported_versions[_VERSION] then
+ print("Unsupported Lua version: " .. _VERSION, "red")
+ panic()
+
+ -- NOTREACHED
+end
+
+do
+ local std = {}
+
+ -- Start by listing the standard utilities we want to copy from the
+ -- main environment. This will depend on the Lua version only.
+
+ do
+ local std = {
+ _G = {"assert", "error", "getfenv", "getmetatable", "ipairs", "load",
+ "loadstring", "next", "pairs", "pcall", "print", "rawequal",
+ "rawget", "rawset", "select", "setfenv", "setmetatable",
+ "tonumber", "tostring", "type", "unpack", "xpcall"},
+ coroutine = {"create", "resume", "running", "status", "wrap",
+ "yield"},
+ string = {"byte", "char", "dump", "find", "format", "gmatch",
+ "gsub", "len", "lower", "match", "rep", "reverse", "sub",
+ "upper"},
+ table = {"concat", "insert", "maxn", "remove", "sort"},
+ math = {"abs", "acos", "asin", "atan", "atan2", "ceil", "cos",
+ "cosh", "deg", "exp", "floor", "fmod", "frexp", "huge",
+ "ldexp", "log", "log10", "max", "min", "modf", "pi", "pow",
+ "rad", "random", "randomseed", "sin", "sinh", "sqrt", "tan",
+ "tanh"},
+ os = {"clock", "date", "difftime", "time"},
+ debug = {"debug", "getfenv", "gethook", "getinfo", "getlocal",
+ "getmetatable", "getregistry", "getupvalue", "setfenv",
+ "sethook", "setlocal", "setmetatable", "setupvalue",
+ "traceback"}
+ }
+
+ if _VERSION ~= "Lua 5.1" then
+ -- Lua 5.2 and above.
+
+ removeValuesFromSequence(std._G, {"setfenv", "getfenv", "unpack",
+ "loadstring"})
+ addValuesToSequence(std._G, {"rawlen"})
+
+ removeValuesFromSequence(std.table, {"maxn"})
+ addValuesToSequence(std.table, {"pack", "unpack"})
+
+ removeValuesFromSequence(std.math, {"log10"})
+
+ removeValuesFromSequence(std.debug, {"getfenv", "setfenv"})
+ addValuesToSequence(std.debug, {"getuservalue", "setuservalue",
+ "upvalueid", "upvaluejoin"})
+
+ std.bit32 = {"arshift", "band", "bnot", "bor", "btest",
+ "bxor", "extract", "replace", "lrotate", "lshift", "rrotate",
+ "rshift"}
+
+ if _VERSION ~= "Lua 5.2" then
+ -- Lua 5.3 only.
+
+ addValuesToSequence(std.coroutine, {"isyieldable"})
+ addValuesToSequence(std.string, {"pack", "packsize", "unpack"})
+ addValuesToSequence(std.table, {"move"})
+ removeValuesFromSequence(std.math, {"atan2", "cosh", "frexp",
+ "ldexp", "pow", "sinh"})
+ addValuesToSequence(std.math, {"maxinteger", "mininteger",
+ "tointeger", "type", "ult"})
+
+ std.utf8 = {"char", "charpattern", "codes", "codepoint",
+ "len", "offset"}
+ std.bit32 = nil
+ end
+ end
+ end
+
+ -- Alright, we have decided on what to pick from the default environment,
+ -- let's take it from the bios and add it to our _G now.
+ -- Note that this code does not remove the function from the original
+ -- `bios` module, just in case.
+
+ do
+ for module, functions in pairs(std) do
+ if module == "_G" or type(bios[module]) == "table" then
+ local src = {}
+ local dest = {}
+
+ if module == "_G" then
+ src = bios
+ dest = G
+ else
+ src = bios[module]
+ G[module] = {}
+ dest = G[module]
+ end
+
+ for _, func_name in pairs(functions) do
+ dest[func_name] = src[func_name]
+ end
+ end
+ end
+ end
+
+ -- From here, if necessary, we can check if functions expected to be here
+ -- are present, and if it's not the case, try to replace them with
+ -- equivalents using other functions.
+end
+
+-- Prepare the machine and peripherals system for ComputerCraft.
+-- The main properties of each device, available for all, are the following:
+--
+-- * `parent`: a reference to the parent bus on which the device is.
+-- Is set to `nil` in case any
+-- * `address`: the address on the parent bus.
+-- * `ids`: the "manufacturer" and "product name" of the device. The first
+-- is usually the name of the mod that brings it, while the product name
+-- is a given name for these.
+-- * `cls`: TODO
+--
+-- TODO: use numerical ids for ids? That would mean this startup script
+-- will have to manage correspondances... which is the case anyway? I don't
+-- really know...
+--
+-- TODO: what about devices that are present on both gateways? Should they
+-- be treated as different devices? Be listed as having two buses?
+
+do
+ local builtin_peripherals = {}
+ local events_by_types = {}
+
+ -- The function to create a peripheral.
+
+ local Peripheral = class({
+ __init = function (self, parent, address, manufacturer, name,
+ interfaces)
+ self.parent = parent
+ self.address = address
+ self.manufacturer = manufacturer
+ self.name = name
+ self.interfaces = interfaces
+ end
+ })
+
+ -- Add the abstract bus.
+
+ local bus = class(Peripheral, {
+ __init = function (self)
+ super(self).__init(nil, nil, "thox", "abstract_bus",
+ {"bus"})
+ end,
+
+ listPeripherals = function (self)
+ local peripherals = {}
+
+ -- We copy the list of builtin peripherals, we do not want
+ -- to return a reference to an internal list, otherwise
+ -- the user could tinker with our internals accidentally.
+
+ for address, peripheral in pairs(builtin_peripherals) do
+ peripherals[address] = peripheral
+ end
+
+ return peripherals
+ end,
+
+ getPeripheral = function (self, address)
+ return builtin_peripherals[address]
+ end
+ })()
+
+ _G.bus = bus
+
+ -- The Timer Management Unit (TMU) peripheral, for setting timers,
+ -- alarms and stuff.
+ --
+ -- We do not actually support alarms here, but remove their support
+ -- from bios functions; we prefer timers anyway.
+
+ do
+ local startTimer = bios.startTimer
+ local cancelTimer = bios.cancelTimer
+
+ local tmu = class(Peripheral, {
+ __init = function (self)
+ super(self).__init(bus, "tmu", "thox", "tmu", {"tmu"})
+ end,
+
+ add = function (self, duration)
+ return startTimer(duration)
+ end,
+
+ cancel = function (self, id)
+ return cancelTimer(duration)
+ end
+ })()
+
+ builtin_peripherals.tmu = tmu
+
+ bios.os.startTimer = nil
+ bios.os.cancelTimer = nil
+ bios.os.setAlarm = nil
+ bios.os.cancelAlarm = nil
+
+ events_by_types.timer = function (event_name, id)
+ return tmu, id
+ end
+ end
+
+ -- The default display peripheral, for input/output operations.
+
+ if bios.term.isColor() then
+
+ else
+ local display = Peripheral(bus, "display", "thox", "monochrome_display",
+ {"display"}, {
+ -- TODO: take the functions from here:
+ -- <https://tweaked.cc/module/term.html#ty:Redirect>
+
+ -- TODO: other things.
+ })
+
+ builtin_peripherals.display = display
+ end
+
+ bios.term = nil
+
+ -- The default keyboard and mouse peripheral, for input operations.
+
+ do
+ local getKeyName = bios.keys.getName
+
+ local kbd = class(Peripheral, {
+ __init = function (self)
+ super(self).__init(bus, "keyboard", "thox", "keyboard",
+ {"keyboard"})
+ end
+ })()
+
+ builtin_peripherals.keyboard = kbd
+
+ events_by_types.key = function (event_name, key_id, is_repeat)
+ if not is_repeat then
+ return kbd, "key", true, getKeyName(key_id)
+ end
+ end
+
+ events_by_types.key_up = function (event_name, key_id)
+ return kbd, "key", false, getKeyName(key_id)
+ end
+
+ events_by_types.paste = function (event_name, text)
+ return kbd, "paste", text
+ end
+
+ local mouse_buttons = {"left", "middle", "right"}
+
+ events_by_types.mouse_click = function (event_name, button, x, y)
+ return kbd, "click", mouse_buttons[button], x, y
+ end
+
+ events_by_types.mouse_drag = function (event_name, button, x, y)
+ return kbd, "drag", mouse_buttons[button], x, y
+ end
+
+ events_by_types.mouse_scroll = function (event_name, up_down, x, y)
+ return kbd, "scroll", up_down, x, y
+ end
+
+ events_by_types.mouse_up = function (event_name, button, x, y)
+ return kbd, "up", mouse_buttons[button], x, y
+ end
+
+ bios.keys = nil
+ end
+
+ -- TODO: plenty of things to do here:
+ --
+ -- * Manage modems.
+ -- * Wired buses (requires to get the peripheral list upvalue...).
+ -- * Disk drives.
+ -- * Command modules
+ -- * etc
+ --
+ -- TODO: monitor_touch events should be "click" events on external
+ -- monitors, no? I don't actually know if that's a good idea or not.
+
+ -- Setup the event system.
+ -- The goal of this system is to associate events with their related
+ -- peripheral as listed previously.
+
+ _G.pull = function ()
+ while true do
+ local event_data = table.pack(os.pullEventRaw())
+ local func = event_by_types[event_data[1]]
+
+ if func then
+ return func(table.unpack(event_data))
+ end
+ end
+ end
+
+ bios.os.pullEvent = nil
+ bios.os.pullEventRaw = nil
+ bios.os.queueEvent = nil
+end
+
+-- Clean the terminal and show that we're booting up.
+-- This is calibrated for ComputerCraft.
+
+do
+ local bperipherals = ""
+
+ do
+ local key, value
+
+ for key, value in pairs(_G.bus.peripherals) do
+ if bperipherals ~= "" then
+ bperipherals = bperipherals .. ", "
+ end
+
+ bperipherals = bperipherals .. key
+ end
+ end
+
+ print("thox startup taking place...", "yellow")
+ print("", "yellow")
+ print(" VERSION = " .. tostring(_VERSION), "yellow")
+ print(" HOST = " .. tostring(bios._HOST), "yellow")
+ print(" BIOS = " .. tostring(bios.os.version()), "yellow")
+ print("", "yellow")
+ print("Detected peripherals: " .. bperipherals, "yellow")
+ print("", "white")
+end
+
+-- Run the system.
+
+setmetatable(_ENV, {__index = G})
+
+--do
+-- local handle = bios.fs.open("/system.lua")
+-- local system = handle:readAll()
+--
+-- handle.close()
+--
+-- load(program, "", "t")()
+--end
diff --git a/pkg/core/startup/startup.lua b/pkg/core/startup/startup.lua
deleted file mode 100644
index 591281e..0000000
--- a/pkg/core/startup/startup.lua
+++ /dev/null
@@ -1,175 +0,0 @@
--- a simple startup script for booting thox in a standardized environment.
---
--- Copyright (C) 2020-2021 Thomas Touhey <thomas@touhey.fr>
--- This file is part of the thox project, which is MIT-licensed.
---
--- This file contains the fundamental loading steps of the OS;
--- see <https://thox.touhey.pro/system/processes.html#startup-and-main-loop>
--- for more information.
---
--- Setup the environment by isolating everything that's non
--- standard in a `bios` module.
-
-do
- local G = {}
- local pairs = _G.pairs
- local setmetatable = _G.setmetatable
-
- -- Start by copying recursively the _G content in the bios module.
-
- do
- local function tcopy(dest, src)
- for key, value in pairs(src) do
- if key == "_G" or key == "_ENV" then
- -- We avoid copying it.
- elseif type(value) == "table" then
- dest[key] = {}
- tcopy(dest[key], value)
- else
- dest[key] = src[key]
- end
- end
- end
-
- G._ENV = _ENV
- G._G = G
- G.bios = {}
- tcopy(G.bios, _G)
- end
-
- do
- local stdfuncs = {
- _G = {"assert", "collectgarbage", "dofile", "error",
- "getmetatable", "ipairs", "load", "loadfile", "next", "pairs",
- "pcall", "print", "rawequal", "rawget", "rawlen", "rawset",
- "select", "setmetatable", "tonumber", "tostring", "type",
- "_VERSION", "xpcall", "require"},
- coroutine = {"create", "isyieldable", "resume", "running",
- "status", "wrap", "yield"},
- package = {"config", "cpath", "loaded", "loadlib", "path",
- "preload", "searchers", "searchpath"},
- string = {"byte", "char", "dump", "find", "format",
- "gmatch", "gsub", "len", "lower", "match", "pack",
- "packsize", "rep", "reverse", "sub", "unpack", "upper"},
- utf8 = {"char", "charpattern", "codes", "codepoint", "len",
- "offset"},
- table = {"concat", "insert", "move", "pack", "remove", "sort",
- "unpack"},
- math = {"abs", "acos", "asin", "atan", "ceil", "cos", "deg",
- "exp", "floor", "fmod", "huge", "log", "max", "maxinteger",
- "min", "mininteger", "modf", "pi", "rad", "random",
- "randomseed", "sin", "sqrt", "tan", "tointeger", "type",
- "ult"},
- io = {"close", "flush", "input", "lines", "open", "output",
- "popen", "read", "tmpfile", "type", "write"},
- os = {"clock", "date", "difftime", "execute", "exit", "getenv",
- "remove", "rename", "setlocale", "time", "tmpname"},
- debug = {"debug", "gethook", "getinfo", "getlocal", "getmetatable",
- "getmetatable", "getregistry", "getupvalue", "getuservalue",
- "sethook", "setlocal", "setmetatable", "setupvalue",
- "setuservalue", "traceback", "upvalueid", "upvaluejoin"},
- }
-
- local function isempty(t)
- for key, _ in pairs(t) do
- return false
- end
-
- return true
- end
-
- for module, functions in pairs(stdfuncs) do
- if module == "_G" or type(G.bios[module]) == "table" then
- local src = {}
- local dest = {}
-
- if module == "_G" then
- src = G.bios
- dest = G
- else
- src = G.bios[module]
- G[module] = {}
- dest = G[module]
- end
-
- for _, func_name in pairs(functions) do
- dest[func_name] = src[func_name]
- src[func_name] = nil
- end
-
- if module ~= "_G" and isempty(G.bios[module]) then
- G.bios[module] = nil
- end
- end
- end
-
- -- Remove a few functions.
-
- if G.string.gmatch == nil then
- G.string.gmatch = G.bios.string.gfind
- end
-
- G.bios.getfenv = nil
- G.bios.setfenv = nil
- G.bios.loadstring = nil
- G.bios.unpack = nil
- G.bios.__inext = nil
- G.bios.printError = nil
- G.bios.write = nil
- G.bios.read = nil
- G.bios.sleep = nil
-
- G.bios.io = nil
- G.bios.debug = nil
- G.bios.coroutine = nil
- G.bios.string = nil
- G.bios.utf8 = nil
- G.bios.math = nil
- G.bios.table = nil
- G.bios.bit = nil
- G.bios.bit32 = nil
- end
-
- setmetatable(_ENV, {__index = G})
-end
-
--- Clean the terminal and show that we're booting up.
-
-do
- local BLACK = 32768
- local WHITE = 1
- local YELLOW = 16
-
- local function setColors(fore, back)
- if bios.term.setBackgroundColor ~= nil then
- bios.term.setBackgroundColor(back)
- end
- if bios.term.setTextColor ~= nil then
- bios.term.setTextColor(fore)
- end
- end
-
- setColors(YELLOW, BLACK)
- bios.term.clear()
- bios.term.setCursorPos(1, 1)
-
- print("Booting up thox...")
- print()
- print(" VERSION = " .. tostring(_VERSION))
- print(" HOST = " .. tostring(bios._HOST))
- print(" BIOS = " .. tostring(bios.os.version()))
- print()
-
- setColors(WHITE, BLACK)
-end
-
--- Run the system.
-
-do
- local handle = bios.fs.open("/system.lua")
- local system = handle:readAll()
-
- handle.close()
-
- load(program, "", "t")()
-end
diff --git a/pkg/core/thox/pkgbuild.lua b/pkg/core/thox/pkgbuild.lua
index 2bddf72..ab1770c 100644
--- a/pkg/core/thox/pkgbuild.lua
+++ b/pkg/core/thox/pkgbuild.lua
@@ -1,5 +1,5 @@
pkgname = "thox"
-pkgver = "2021-03-29-01"
+pkgver = "2021032901"
pkgrel = 0
pkgdesc = "thox kernel"
url = "https://thox.madefor.cc/system/process.html"
diff --git a/pkg/core/thox/system.lua b/pkg/core/thox/system.lua
index 1b99323..276d174 100644
--- a/pkg/core/thox/system.lua
+++ b/pkg/core/thox/system.lua
@@ -63,13 +63,15 @@ do
-- An answer to an RPC procedure call has been emitted.
ANSWER = 3,
- -- An RPC procedure binding has been requested for
- -- the current process.
- BIND = 4,
+ -- A call to pull events has been emitted, with
+ -- filters.
+ PULL = 4,
- -- An RPC procedure unbinding has been requested for
- -- the current process.
- UNBIND = 5,
+ -- A call to spawn a process has been emitted.
+ SPAWN = 5,
+
+ -- A call to exit self has been emitted.
+ EXIT = 6,
}
-- Process definition.
@@ -149,6 +151,7 @@ do
spawn = function (self, program, options)
local pid, env
+ local globals
-- First of all, get the next identifier available.
-- TODO: find a less dumb way to find a free pid?
@@ -174,340 +177,376 @@ do
--
-- * https://github.com/SquidDev-CC/CC-Tweaked/blob/mc-1.15.x/src/main/resources/data/computercraft/lua/bios.lua#L525
- do
- local globals = {}
- env = {}
+ globals = {}
+ env = {}
- setmetatable(env, {
- __index = globals})
- env._ENV = env
- env._G = globals
+ setmetatable(env, {
+ __index = globals})
+ env._ENV = env
+ env._G = globals
- -- Here, we create the basic environment. This chunk actually
- -- depends on the process type:
- --
- -- * kernel processes start with all of the available APIs,
- -- including the native Java APIs.
- -- * user processes only start with functions defined as
- -- safe for sandboxing, as they do not influence other;
- -- processes nor the process manager we're currently
- -- defining.
- --
- -- References:
- --
- -- * http://lua-users.org/wiki/SandBoxes
- -- * https://www.lua.org/manual/5.3/manual.html#6
- -- * https://github.com/SquidDev-CC/CC-Tweaked/blob/mc-1.15.x/src/main/resources/data/computercraft/lua/bios.lua
-
- if isKernel then
- local function rcopy(dest, src)
- for key, value in pairs(src) do
- if key == "_G" or key == "_ENV" then
- -- We avoid copying it.
- elseif type(value) == "table" then
- dest[key] = {}
- rcopy(dest[key], value)
- else
- dest[key] = src[key]
- end
- end
- end
-
- for _, key in pairs({"assert", "error", "ipairs",
- "next", "pairs", "pcall", "tonumber", "tostring",
- "type", "_VERSION", "xpcall"}) do
- globals[key] = _G[key]
- end
+ -- Here, we create the basic environment. This chunk actually
+ -- depends on the process type:
+ --
+ -- * kernel processes start with all of the available APIs,
+ -- including the native Java APIs.
+ -- * user processes only start with functions defined as
+ -- safe for sandboxing, as they do not influence other;
+ -- processes nor the process manager we're currently
+ -- defining.
+ --
+ -- References:
+ --
+ -- * http://lua-users.org/wiki/SandBoxes
+ -- * https://www.lua.org/manual/5.3/manual.html#6
+ -- * https://github.com/SquidDev-CC/CC-Tweaked/blob/mc-1.15.x/src/main/resources/data/computercraft/lua/bios.lua
+ --
+ -- TODO: review what should be provided here.
- for _, module in pairs({"os", "string", "table",
- "math", "utf8", "coroutine"}) do
- globals[key] = {}
+ do
+ local function rcopy(dest, src)
+ for key, value in pairs(src) do
+ if key == "_G" or key == "_ENV" then
+ -- We avoid copying it.
+ elseif type(value) == "table" then
+ dest[key] = {}
+ rcopy(dest[key], value)
+ else
+ dest[key] = src[key]
+ end
end
+ end
- for _, key in pairs({"clock", "difftime", "time"}) do
- globals.os[key] = _G.os[key]
- end
+ for _, key in pairs({"assert", "error", "ipairs",
+ "next", "pairs", "pcall", "tonumber", "tostring",
+ "type", "_VERSION", "xpcall"}) do
+ globals[key] = _G[key]
+ end
- for _, key in pairs({"byte", "char", "find", "format",
- "gmatch", "gsub", "len", "lower", "match",
- "rep", "reverse", "sub", "upper"}) do
- globals.string[key] = _G.string[key]
- end
+ for _, module in pairs({"os", "string", "table",
+ "math", "utf8", "coroutine"}) do
+ globals[key] = {}
+ end
- for _, key in pairs({"insert", "maxn", "remove",
- "sort"}) do
- globals.table[key] = _G.table[key]
- end
+ for _, key in pairs({"clock", "difftime", "time"}) do
+ globals.os[key] = _G.os[key]
+ end
- for _, key in pairs({"abs", "acos", "asin", "atan",
- "atan2", "ceil", "cos", "cosh", "deg", "exp",
- "floor", "fmod", "frexp", "huge", "ldexp", "log",
- "log10", "max", "min", "modf", "pi", "pow", "rad"}) do
- globals.math[key] = _G.math[key]
- end
+ for _, key in pairs({"byte", "char", "find", "format",
+ "gmatch", "gsub", "len", "lower", "match",
+ "rep", "reverse", "sub", "upper"}) do
+ globals.string[key] = _G.string[key]
+ end
- for _, key in pairs({"char", "charpattern", "codes",
- "codepoint", "len", "offset"}) do
- globals.utf8[key] = _G.utf8[key]
- end
+ for _, key in pairs({"insert", "maxn", "remove",
+ "sort"}) do
+ globals.table[key] = _G.table[key]
+ end
- for name, func in pairs(_G.coroutine) do
- globals.coroutine[name] = func
- end
+ for _, key in pairs({"abs", "acos", "asin", "atan",
+ "atan2", "ceil", "cos", "cosh", "deg", "exp",
+ "floor", "fmod", "frexp", "huge", "ldexp", "log",
+ "log10", "max", "min", "modf", "pi", "pow", "rad"}) do
+ globals.math[key] = _G.math[key]
+ end
- -- TODO: we do not always want to copy the bios
- -- functions to the process, permissions would be
- -- welcome I think.
+ for _, key in pairs({"char", "charpattern", "codes",
+ "codepoint", "len", "offset"}) do
+ globals.utf8[key] = _G.utf8[key]
+ end
- globals.bios = {}
- rcopy(globals.bios, _G.bios)
+ for name, func in pairs(_G.coroutine) do
+ globals.coroutine[name] = func
end
- -- We actually need to hijack the normal work of the
- -- coroutine module to inject a few things.
- -- Indeed, when coroutine yields, with thox, it can be
- -- one of two things:
- --
- -- * A system call, as thox makes use of coroutines for
- -- its threads.
- -- * A normal yield, for internal coroutines uses.
- --
- -- For hijacking, we actually yield a first argument that
- -- is hidden to the user, which is 1 in the first case
- -- and 0 in the second; unless the function yields when
- -- returning, in which case there is no special argument
- -- because it can not be a system call.
- --
- -- References:
- --
- -- * http://www.lua.org/manual/5.3/manual.html#2.6
- -- * http://www.lua.org/manual/5.3/manual.html#6.2
+ -- TODO: we do not always want to copy the bios
+ -- functions to the process, permissions would be
+ -- welcome I think.
- do
- local cocreate = coroutine.create
- local coresume = coroutine.resume
- local coyield = coroutine.yield
- local costatus = coroutine.status
- local prevent_from_hanging = function (func)
- return self:prevent_from_hanging(func)
- end
+ globals.bios = {}
+ rcopy(globals.bios, _G.bios)
+ end
- globals.coroutine.create = function (func)
- return prevent_from_hanging(cocreate(func))
- end
+ -- We actually need to hijack the normal work of the
+ -- coroutine module to inject a few things.
+ -- Indeed, when coroutine yields, with thox, it can be
+ -- one of two things:
+ --
+ -- * A system call, as thox makes use of coroutines for
+ -- its threads.
+ -- * A normal yield, for internal coroutines uses.
+ --
+ -- For hijacking, we actually yield a first argument that
+ -- is hidden to the user, which is 1 in the first case
+ -- and 0 in the second; unless the function yields when
+ -- returning, in which case there is no special argument
+ -- because it can not be a system call.
+ --
+ -- References:
+ --
+ -- * http://www.lua.org/manual/5.3/manual.html#2.6
+ -- * http://www.lua.org/manual/5.3/manual.html#6.2
- globals.coroutine.yield = function (...)
- local args = table.pack(...)
- table.insert(args, 1, YIELD.BASIC)
+ do
+ local cocreate = coroutine.create
+ local coresume = coroutine.resume
+ local coyield = coroutine.yield
+ local costatus = coroutine.status
+ local prevent_from_hanging = function (func)
+ return self:prevent_from_hanging(func)
+ end
- return coyield(table.unpack(args))
- end
+ globals.coroutine.create = function (func)
+ return prevent_from_hanging(cocreate(func))
+ end
- globals.coroutine.resume = function (co, ...)
- local args = table.pack(...)
+ globals.coroutine.yield = function (...)
+ local args = table.pack(...)
+ table.insert(args, 1, YIELD.BASIC)
- while true do
- local retpack = table.pack(coresume(co,
- table.unpack(args)))
+ return coyield(table.unpack(args))
+ end
- if costatus(co) == "dead" then
- -- The function returned its final yield,
- -- which means we just return what it has
- -- returned.
+ globals.coroutine.resume = function (co, ...)
+ local args = table.pack(...)
- return table.unpack(retpack)
- elseif retpack[1] == true then
- -- The function has just yielded, which
- -- means the special argument is there,
- -- let's treat it!
+ while true do
+ local retpack = table.pack(coresume(co,
+ table.unpack(args)))
- local ret = table.remove(retpack, 1)
- local typ = table.remove(retpack, 1)
+ if costatus(co) == "dead" then
+ -- The function returned its final yield,
+ -- which means we just return what it has
+ -- returned.
- if typ == YIELD.BASIC then
- -- A normal value.
+ return table.unpack(retpack)
+ elseif retpack[1] == true then
+ -- The function has just yielded, which
+ -- means the special argument is there,
+ -- let's treat it!
- table.insert(retpack, 1, ret)
- return table.unpack(retpack)
- else
- -- A system yield, we just yield it for
- -- the parent coroutine to catch it to
- -- transmit it to the main loop, then
- -- come back here.
-
- table.insert(retpack, 1, typ)
- args = table.pack(coyield(
- table.unpack(retpack)))
- end
- else
- -- An error has occurred, so we just
- -- return it as is.
+ local ret = table.remove(retpack, 1)
+ local typ = table.remove(retpack, 1)
+ if typ == YIELD.BASIC then
+ -- A normal value.
+
+ table.insert(retpack, 1, ret)
return table.unpack(retpack)
+ else
+ -- A system yield, we just yield it for
+ -- the parent coroutine to catch it to
+ -- transmit it to the main loop, then
+ -- come back here.
+
+ table.insert(retpack, 1, typ)
+ args = table.pack(coyield(
+ table.unpack(retpack)))
end
+ else
+ -- An error has occurred, so we just
+ -- return it as is.
+
+ return table.unpack(retpack)
end
end
+ end
- -- System calls.
+ -- System calls.
- local toCanonicalName, toValidName, toValidArgs
+ local toCanonicalName, toValidName, toValidArgs
- do
- local vc = "[a-z][a-z0-9_]*"
- local vn = ("^" .. vc .. "(.(" .. vc .. ".)*"
- .. vc .. ")?$")
- local reservedNames = {"", "and", "break", "do",
- "else", "elseif", "end", "false", "for",
- "function", "goto", "if", "in", "local", "nil",
- "not", "or", "repeat", "return", "then", "true",
- "until", "while"}
+ do
+ local vc = "[a-z][a-z0-9_]*"
+ local vn = ("^" .. vc .. "(.(" .. vc .. ".)*"
+ .. vc .. ")?$")
+ local reservedNames = {"", "and", "break", "do",
+ "else", "elseif", "end", "false", "for",
+ "function", "goto", "if", "in", "local", "nil",
+ "not", "or", "repeat", "return", "then", "true",
+ "until", "while"}
+
+ toCanonicalName = function (name)
+ name = string.lower(tostring(name))
+ if name == "" then
+ return nil
+ end
+
+ return name
+ end
+
+ toValidName = function (name)
+ name = string.lower(tostring(name))
- toCanonicalName = function (name)
- name = string.lower(tostring(name))
- if name == "" then
+ if string.match(name, vn) ~= name then
+ return nil
+ end
+
+ for _, reserved in pairs(reservedNames) do
+ if string.find("." .. name .. ".",
+ "." .. reserved .. ".") ~= nil then
return nil
end
-
- return name
end
- toValidName = function (name)
- name = string.lower(tostring(name))
+ return name
+ end
+
+ toValidArgs = function (args)
+ local sanitizedArgs = {}
+
+ -- What we're trying to do here is to eliminate
+ -- complicated types to only keep simple types
+ -- as arguments, for now.
+ --
+ -- For now, we do not allow tables because they
+ -- can have custom metatables. This is not what
+ -- we want for now.
+
+ for i, value in ipairs(args) do
+ local typ = type(value)
- if string.match(name, vn) ~= name then
+ if typ == "number"
+ or typ == "string"
+ or typ == "boolean" then
+ table.insert(sanitizedArgs, value)
+ else
return nil
end
+ end
- for _, reserved in pairs(reservedNames) do
- if string.find("." .. name .. ".",
- "." .. reserved .. ".") ~= nil then
- return nil
- end
- end
+ return sanitizedArgs
+ end
+ end
- return name
- end
+ -- Process-related calls.
- toValidArgs = function (args)
- local sanitizedArgs = {}
+ local run_func(func, options)
+ -- TODO: validate that ``func`` is a function,
+ -- has no upvalues (or upvalues are replaced
+ -- with nil), and that options is a table and
+ -- copy the values instead of transmitting them
+ -- by reference.
- -- What we're trying to do here is to eliminate
- -- complicated types to only keep simple types
- -- as arguments, for now.
- --
- -- For now, we do not allow tables because they
- -- can have custom metatables. This is not what
- -- we want for now.
+ return coyield(YIELD.SPAWN, func, {})
+ end
- for i, value in ipairs(args) do
- local typ = type(value)
+ globals.os.run = run_func
- if typ == "number"
- or typ == "string"
- or typ == "boolean" then
- table.insert(sanitizedArgs, value)
- else
- return nil
- end
- end
+ globals.os.run_file = function (path, options)
+ local path = ("\"" ..
+ string.gsub(string.gsub(path, "\\", "\\\\"),
+ "\"", "\\\"") .. "\"")
+ local code = ("local w = load(io.open(" .. path .. ")" ..
+ ":read(\"a\"), " .. path .. ", \"t\")" ..
+ "\r\nif w == nil then os.exit(false, true) end" ..
+ "\r\nreturn w()")
- return sanitizedArgs
- end
- end
+ return run_func(load(code, "t"))
+ end
- globals.os.bind = function (name)
- local name = toValidName(name)
- if name == nil then
- return false
- end
+ globals.os.run_code = function (code, options)
+ return run_func(load(code, "t"))
+ end
- return coyield(YIELD.BIND, name)
- end
+ -- Context-related calls.
- globals.os.unbind = function (name)
- local name = toCanonicalName(name)
- if name == nil then
- return false
- end
+ globals.os.context = function ()
+ -- TODO: create a context, add a reference to it
+ -- in the current process, and return its number.
- return coyield(YIELD.UNBIND, name)
- end
+ error("not implemented")
+ end
- globals.os.call = function (name, ...)
- local name = toCanonicalName(name)
- local args = toValidArgs(table.unpack(...))
+ globals.os.close = function (ctx)
+ -- TODO: look for a context with that number, and
+ -- close it.
- if name == nil or args == nil then
- return false
- end
+ error("not implemented")
+ end
- return coyield(YIELD.CALL, name, table.unpack(args))
- end
+ -- Event-related calls.
- globals.os.answer = function (id, ...)
- local id = math.tointeger(id)
- local args = toValidArgs(table.unpack(...))
+ globals.os.pull = function (...)
+ -- TODO: validate the filters.
- if name == nil or args == nil then
- return false
- end
+ return coyield(YIELD.PULL)
+ end
- return coyield(YIELD.ANSWER, name, table.unpack(args))
+ -- RPC interface.
+
+ globals.os.call = function (ctx, name, ...)
+ local name = toCanonicalName(name)
+ local args = toValidArgs(table.unpack(...))
+
+ if name == nil or args == nil then
+ return false
end
- globals.os.pull = function ()
- return coyield(YIELD.CALL, "pull")
+ -- TODO: check if the given context exists.
+
+ return coyield(YIELD.CALL, name, table.unpack(args))
+ end
+
+ globals.os.answer = function (cid, ...)
+ local cid = math.tointeger(cid)
+ local args = toValidArgs(table.unpack(...))
+
+ if name == nil or args == nil then
+ return false
end
- globals.os.capture = function (co, ...)
- local args = table.pack(...)
+ return coyield(YIELD.ANSWER, name, table.unpack(args))
+ end
- while true do
- local retpack = table.pack(coresume(co,
- table.unpack(args)))
+ globals.os.capture = function (co, ...)
+ local args = table.pack(...)
- if costatus(co) == "dead" then
- -- The function returned its final yield,
- -- which means we just return what it has
- -- returned.
+ while true do
+ local retpack = table.pack(coresume(co,
+ table.unpack(args)))
- return "yield", table.unpack(retpack)
- elseif retpack[1] == true then
- -- The function has just yielded, which
- -- means the special argument is there,
- -- let's treat it!
+ if costatus(co) == "dead" then
+ -- The function returned its final yield,
+ -- which means we just return what it has
+ -- returned.
- local ret = table.remove(retpack, 1)
- local typ = table.remove(retpack, 1)
+ return "yield", table.unpack(retpack)
+ elseif retpack[1] == true then
+ -- The function has just yielded, which
+ -- means the special argument is there,
+ -- let's treat it!
- if typ == YIELD.BASIC then
- -- A normal value.
+ local ret = table.remove(retpack, 1)
+ local typ = table.remove(retpack, 1)
- table.insert(retpack, 1, ret)
- return "yield", table.unpack(retpack)
- elseif typ == YIELD.PREEMPT then
- -- Pre-empt, let's just yield for this one.
+ if typ == YIELD.BASIC then
+ -- A normal value.
- coyield(YIELD.PREEMPT)
- elseif typ == YIELD.CALL then
- -- A call, we want to return it!
+ table.insert(retpack, 1, ret)
+ return "yield", table.unpack(retpack)
+ elseif typ == YIELD.PREEMPT then
+ -- Pre-empt, let's just yield for this one.
- return "call", table.unpack(retpack)
- elseif typ == YIELD.ANSWER then
- -- An answer, we want to return it as well.
+ coyield(YIELD.PREEMPT)
+ elseif typ == YIELD.CALL then
+ -- A call, we want to return it!
- return "answer", table.unpack(retpack)
- elseif typ == YIELD.BIND then
- -- A bind, we want to return it as well.
+ return "call", table.unpack(retpack)
+ elseif typ == YIELD.ANSWER then
+ -- An answer, we want to return it as well.
- return "bind", retpack[1]
- end
- else
- -- An error has occurred, so we just
- -- return it as is.
+ return "answer", table.unpack(retpack)
+ elseif typ == YIELD.BIND then
+ -- A bind, we want to return it as well.
- return table.unpack(retpack)
+ return "bind", retpack[1]
end
+ else
+ -- An error has occurred, so we just
+ -- return it as is.
+
+ return table.unpack(retpack)
end
end
end
@@ -615,30 +654,12 @@ do
-- TODO: check for debug functions, maybe?
end
- -- The environment is ready, now we can load the program
- -- with the environment and check if it has loaded correctly.
- --
- -- TODO: name the coroutine!
- -- TODO: actually load the file from the coroutine?
- -- Not sure about that one yet, but it could be a good idea.
-
- local chunk
-
- do
- local err
-
- chunk, err = load(program, "", "t", env)
- if chunk == nil then
- error("could not load the program: " .. err)
- end
- end
-
-- The program has been loaded with the correct environment,
-- we can now create the process with the given function.
local p = Process()
- p.thread = self:prevent_from_hanging(coroutine.create(chunk))
+ p.thread = self:prevent_from_hanging(coroutine.create(func))
p.state = Process.RUNNING
self.p[pid] = p