This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Xfel's profile picture

Correct API loading

Started by Xfel, 21 April 2012 - 07:00 AM
Xfel #1
Posted 21 April 2012 - 09:00 AM
currently, when loading an API, the loader takes the contents of the function environment and puts them into the api table. This doesn't match the lua specification, which says that a api/module file should return the API table. Additionally, it leads to confusion because of statements like this:

-- In an api file "foobar"
function foo(...)
 return foobar.bar(...) -- Has to prefix the call
end

local metatable = {
 __index = foo -- Can't use prefix here.
}

function bar(...)
 return setmetatable({},metatable)
end

Aditionally, the current code won't work anymore in Lua 5.2, which I hope will be used some day.

So my request is to re-design the api loading process to match the lua spec.
Some kind of dynamic API loading with an automated search would also be a good thing.

These changes would only affect the lua code.
Xfel #2
Posted 21 April 2012 - 11:26 AM
I made up some code which could be used instead of the old os.loadAPI part. It can't be inserted exactly there, but that's the general idea. The package api should also not be protected like the others, as it contains variables (like package.path) which are meant to be changeable.

this version will also search in a local directory ("/apis").

Please don't reply things like "If you want it, just change it on your cc installation". My request is a global standard for api loading.


-- An example for how to include package lib support
-- This would be inserted into bios.lua instead of os.loadAPI/os.unloadAPI

-- constants
package = {}
package.config = [[/
:
?
]]
package.path = "./?:.?./lua:/apis/?:/apis/?.lua:/rom/apis/?:/rom:/apis/?.lua"
if turtle then
package.path = package.path .. ":/rom/apis/turtle/?:/rom:/apis/turtle/?.lua"
end
-- loaded/preload can't be overridden. their localness also improves the speed.
local loaded = {}
local preload = {}
package.loaded = loaded
package.preload = preload
-- Init loaded native apis
for k,v in pairs( _G ) do
if type(k) == "string" and type(v) == "table" and k ~= "_G" then
  loaded[k] = v
end
end

-- define the searcher order
package.searchers = {
-- search preload
function(modname)
  return preload[modname]
end,
-- search path
function(modname)
  local file = package.searchpath(modname, path)
  if not file then
   return nil
  end
  -- load the lua file
  local func, err = loadfile(file)
  if func then
   -- successfully loaded
   return func, fs.getName(file)
  else
   error(err)
  end
end
}

local function getconf(line)
for opt in string.gmatch(package.config, "[^\n]" do
  if line == 1 then
   return opt
  end
  line = line - 1
end
end
function package.searchpath(name, path, sep, rep)
-- replace directory pattern
sep = sep or "."
rep = rep or getconf(1)

local nameWithRep = string.gsub(name, "%" .. sep, rep)

local qmpattern = "%" .. getconf(3)

-- save the attempts for the error message
local attempts = {}

for pPath in string.gmatch(path, "[^" .. getconf(2) .. "]+") do
  -- inject module name
  pPath = shell.resolve(string.gsub(pPath, qmpattern, nameWithRep))

  if fs.exists( sPath ) and not fs.isDir( sPath ) then
   return sPath
	 end

  -- add to missed attempts
  table.insert(attempts, pPath)
end

return nil, table.concat(attempts, ", ")
end

function require(modname)
if type(modname) ~= "string" then
  error("Invalid arg; string expected")
end

-- check for already loaded libraries
local lib = loaded[modname]

if lib then
  return lib
end

-- find a loader
local loader, source
for _,s in ipairs(package.searchers)
  loader, source = s(modname)
  if loader then
   break
  end
end

if not loader then
  error("Module not available: " .. modname)
end

-- load the library
lib = loader(modname, source) or true -- if the loader returns nil, use true instead

-- save for later use
loaded[modname] = lib
return lib
end
-- some apis need to be loaded early
term = require "term"
rednet = require "rednet"
textutils = require "textutils"
if turtle then
require "turtle"
end