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

API Unpack/Writing

Started by KingofGamesYami, 13 May 2014 - 02:44 AM
KingofGamesYami #1
Posted 13 May 2014 - 04:44 AM
I've been researching this function, but from the descriptions I have found, I still have no idea if it would do what I want it to do. Could you (in theory) use this to create functions within an api?

--"random" api
for k, v in pairs(fs.list("rom/apis")) do
 setFenv(k, function() print(k) end)
end
--end api
--start program "useless"
os.loadAPI("random")
random.math()
--end program

>random --*enter*
math
>_
Edited on 14 May 2014 - 08:50 PM
theoriginalbit #2
Posted 13 May 2014 - 05:56 AM
setfenv stands for set function environment. have a read of this.
KingofGamesYami #3
Posted 13 May 2014 - 01:40 PM
In that case, I don't think it would work for what I am doing. I am attempting to create an api that writes itself, if that makes any sense.

local f = {}
for k, v in pairs( fs.list( "rom/apis" ) ) do
 f[k] = function() print(k) end
end
setmetatable(f, {__index = _G}
setfenv(f)
would something like this work?
CometWolf #4
Posted 13 May 2014 - 03:42 PM
Just try it out if you wanna know wether it works or not!
fs.list returns a numerically indexed table, so you would just get a bunch of f[1], f[2] functions that prints their number. I doubt that's the desired result. And you would lose direct access to all APIs, and calling them would instead result in your print function, indexing them would error. And you would have to use _G.apiname to access any of them.
KingofGamesYami #5
Posted 13 May 2014 - 10:10 PM
Hmm… Well, what I want to do is this:

local apis = {}
for k, v in pairs( fs.list( "rom/apis" ) )
 apis[v] = print( v )
end
so that my api will contain api.redstone(), api.color(), etc. If I can figure this out, I can make the rest of my code work relatively easily.
MKlegoman357 #6
Posted 13 May 2014 - 10:12 PM
So you want your API to have all native-CC pure-Lua APIs inside your API? And if so, why?
Edited on 13 May 2014 - 08:17 PM
KingofGamesYami #7
Posted 13 May 2014 - 10:48 PM
So you want your API to have all native-CC pure-Lua APIs inside your API? And if so, why?
Yeah, exactly. I am updating wireless peripherals to control anything you want. Obviously stuff like colors and colours will never be used, but I want to reduce file size as much as possible. Also it's just an interesting concept, an api that builds itself…
Spoiler

local function createWireless( api )
	local wireless = { }
	for k, v in pairs( api ) do
		wireless[ k ] = function( self, ... )
			modem.transmit( self.ca, self.cb, { type = tostring( api ), method = k, args = { ... } } )
			local event
			repeat
				event = { os.pullEvent() }
			until event[ 5 ].type == tostring( api )
			return unpack( event )
		end
	end
	return wireless
end

local apis = { }
local tApis = fs.list( "rom/apis" )

for n,sFile in ipairs( tApis ) do
    if string.sub( sFile, 1, 1 ) ~= "." then
        local sPath = fs.combine( "rom/apis", sFile )
        if not fs.isDir( sPath ) then
            apis[ sPath ] = sPath
        end
    end
end

if not turtle then
	apis[ "turtle" ] = {
	craft = "craft",
	forward = "forward",
	back = "back",
	up = "up",
	down = "down",
	turnLeft = "turnLeft",
	turnRight = "turnRight",
	select = "select",
	getSelectedSlot = "getSelectedSlot",
	getItemSpace = "getItemSpace",
	equipLeft = "equipLeft",
	equipRight = "equipRight",
	attack = "attack",
	attackUp = "attackUp",
	dig = "dig",
	digUp = "digUp",
	digDown = "digDown",
	place = "place",
	placeUp = "placeUp",
	placeDown = "placeDown",
	detect = "detect",
	detectUp = "detectUp",
	detectDown = "detectDown",
	compare = "compare",
	compareUp = "compareUp",
	compareDown = "compareDown",
	compareTo = "compareTo",
	drop = "drop",
	dropUp = "dropUp",
	dropDown = "dropDown",
	suck = "suckdown",
	refuel = "refuel",
	getFuelLevel = "getFuelLevel",
	getFuelLimit = "getFuelLimit",
	transferTo = "transferTo",
	}
end
apis.peripheral = nil

for k, v in pairs( apis ) do
	if type( k ) == "string" and type( v ) == "table" then
		function k ( ca, cb )
			local per = createWireless( v )
			local tbl = {
			ca = ca,
			cb = cb,
			}
			setmetatable( tbl, { __index = per } )
			return tbl
		end
	end
end

function wrap()
	return apis
end

--[[  Example of usage:

wc = wc.wrap()
turtle = wc.turtle( 204, 204 )
turtle.forward()

]]
CometWolf #8
Posted 13 May 2014 - 11:04 PM
just passing your function the name of the API is enough.

local API = _G[APIname]
KingofGamesYami #9
Posted 13 May 2014 - 11:41 PM
How to get the api name though? I don't think I can use shell.getRunning() since that would return the program loading my API. And of course, I don't want to inconvenience the users by making them load it differently.
CometWolf #10
Posted 14 May 2014 - 12:21 AM
gotta get creative.

for k,v in pairs(_G) do
  if type(v) == "table" then
    --k is an API name
  end
end
KingofGamesYami #11
Posted 14 May 2014 - 03:11 AM
So you want your API to have all native-CC pure-Lua APIs inside your API? And if so, why?
Yeah, exactly. I am updating wireless peripherals to control anything you want. Obviously stuff like colors and colours will never be used, but I want to reduce file size as much as possible. Also it's just an interesting concept, an api that builds itself…
Spoiler

local function createWireless( api )
	local wireless = { }
	for k, v in pairs( api ) do
		wireless[ k ] = function( self, ... )
			modem.transmit( self.ca, self.cb, { type = tostring( api ), method = k, args = { ... } } )
			local event
			repeat
				event = { os.pullEvent() }
			until event[ 5 ].type == tostring( api )
			return unpack( event )
		end
	end
	return wireless
end

local apis = { }
local tApis = fs.list( "rom/apis" )

for n,sFile in ipairs( tApis ) do
    if string.sub( sFile, 1, 1 ) ~= "." then
        local sPath = fs.combine( "rom/apis", sFile )
        if not fs.isDir( sPath ) then
            apis[ sPath ] = sPath
        end
    end
end

if not turtle then
	apis[ "turtle" ] = {
	craft = "craft",
	forward = "forward",
	back = "back",
	up = "up",
	down = "down",
	turnLeft = "turnLeft",
	turnRight = "turnRight",
	select = "select",
	getSelectedSlot = "getSelectedSlot",
	getItemSpace = "getItemSpace",
	equipLeft = "equipLeft",
	equipRight = "equipRight",
	attack = "attack",
	attackUp = "attackUp",
	dig = "dig",
	digUp = "digUp",
	digDown = "digDown",
	place = "place",
	placeUp = "placeUp",
	placeDown = "placeDown",
	detect = "detect",
	detectUp = "detectUp",
	detectDown = "detectDown",
	compare = "compare",
	compareUp = "compareUp",
	compareDown = "compareDown",
	compareTo = "compareTo",
	drop = "drop",
	dropUp = "dropUp",
	dropDown = "dropDown",
	suck = "suckdown",
	refuel = "refuel",
	getFuelLevel = "getFuelLevel",
	getFuelLimit = "getFuelLimit",
	transferTo = "transferTo",
	}
end
apis.peripheral = nil

for k, v in pairs( apis ) do
	if type( k ) == "string" and type( v ) == "table" then
		function k ( ca, cb )
			local per = createWireless( v )
			local tbl = {
			ca = ca,
			cb = cb,
			}
			setmetatable( tbl, { __index = per } )
			return tbl
		end
	end
end

function wrap()
	return apis
end

--[[  Example of usage:

wc = wc.wrap()
turtle = wc.turtle( 204, 204 )
turtle.forward()

]]
gotta get creative.

for k,v in pairs(_G) do
  if type(v) == "table" then
    --k is an API name
  end
end
In this code I am already finding the name of the api, I meant how do I know what the user named my api that is doing this
awsmazinggenius #12
Posted 14 May 2014 - 03:47 AM
Why not hook into os.loadAPI()?

local oldLoad = os.loadAPI
function os.loadAPI(apiname)
  oldLoad(apiname)
  NAME_OF_API = apiname -- make a global var with the name of the api
end

-- Just have the user do this at the top of their code
function os.loadAPI(apiname) oldLoad(apiname) NAME_OF_API = apiname end
KingofGamesYami #13
Posted 14 May 2014 - 04:28 AM
Why not hook into os.loadAPI()?

local oldLoad = os.loadAPI
function os.loadAPI(apiname)
  oldLoad(apiname)
  NAME_OF_API = apiname -- make a global var with the name of the api
end

-- Just have the user do this at the top of their code
function os.loadAPI(apiname) oldLoad(apiname) NAME_OF_API = apiname end
hmm… could do that. or I could just make my api return

return function() oldLoad(NAME_OF_API) end
awsmazinggenius #14
Posted 14 May 2014 - 05:25 AM
Or, make your API have a so-called Init method.

-- API file
function init()
  dostring("code that makes a table containing your api code. you now know what the table for your API is called"
end
-- Program
os.loadAPI("someobscurefile")
someobscurefile.init()
Good_API_Name.someFunction()
Edited on 14 May 2014 - 03:26 AM
CometWolf #15
Posted 14 May 2014 - 05:54 AM
Why do you need to know the name though?
KingofGamesYami #16
Posted 14 May 2014 - 01:36 PM
just passing your function the name of the API is enough.

local API = _G[APIname]
Why do you need to know the name though?
Doesn't that bit of code need the api name?
CometWolf #17
Posted 14 May 2014 - 02:27 PM
Well yes, for the API you wish to access, but you're saying you want the name of your own API. Why would you need to access your own API, from within itself?
theoriginalbit #18
Posted 14 May 2014 - 02:54 PM
Why would you need to access your own API, from within itself?
to add to that, why would you need to access your own API, from within itself, where you couldn't just invoke the function directly?
KingofGamesYami #19
Posted 14 May 2014 - 10:06 PM
I tried that… declaring the functions in a loop didn't work

for k, v in pairs( table_of_apis ) do
 function v(...) --rest of stuff
 end
end
neither did

for k, v in pairs( table_of_apis ) do
 v = function(...) --rest of stuff
 end
end
CometWolf #20
Posted 14 May 2014 - 10:36 PM
Note that i said "k is an API name" meaning you'd have to use that.
gotta get creative.

for k,v in pairs(_G) do
  if type(v) == "table" then
	--k is an API name
  end
end


local env = getfenv() --api environment
for k,v in pairs(_G) do
  if type(v) == "table" then --keep in mind that _G has more than just APIs
    env[k] = function(...)
	  --function
    end
  end
end

You still didn't answer why you needed the filename of your own API though.
Edited on 14 May 2014 - 08:37 PM
KingofGamesYami #21
Posted 14 May 2014 - 10:43 PM
Note that i said "k is an API name" meaning you'd have to use that.
gotta get creative.

for k,v in pairs(_G) do
  if type(v) == "table" then
	--k is an API name
  end
end


local env = getfenv() --api environment
for k,v in pairs(_G) do
  if type(v) == "table" then --keep in mind that _G has more than just APIs
    env[k] = function(...)
	  --function
    end
  end
end

You still didn't answer why you needed the filename of your own API though.
I was stating code I had previously tried, before posting this topic. I need the name of my api to put in

local API = _G[APIname]
unless I misunderstand what exactly this is doing.

Also, to get apis, since I only need the default ones… I use this:

apis = fs.list( "rom/apis" )
if not fs.isDir( apis[k] ) and k:sub(1, 1) ~= "." then
 --it's an api
end
which I pulled from the actual computercraft files.
Edited on 14 May 2014 - 08:46 PM
CometWolf #22
Posted 14 May 2014 - 10:56 PM
I was stating code I had previously tried, before posting this topic. I need the name of my api to put in

local API = _G[APIname]
unless I misunderstand what exactly this is doing.
This would be the same as

local API = getfenv()
Provided it's run from within your API with os.loadAPI. But again, what is the point? You might aswell just do
derp = whatever,
instead of API.derp = whatever

Also, to get apis, since I only need the default ones… I use this:

apis = fs.list( "rom/apis" )
if not fs.isDir( apis[k] ) and k:sub(1, 1) ~= "." then
--it's an api
end
which I pulled from the actual computercraft files.
This wouldn't even work, since you appear to be missing the for loop, and fs.isDir requires absolute paths. Even if those mistakes were corrected, it would still be heavier computation wise than my approach. Also, not all the APIs are present in the apis folder. os being a qucik example from the top of my head. Why would you only want default APIs though? I want to know what this code is actually for
Edited on 14 May 2014 - 09:10 PM
KingofGamesYami #23
Posted 14 May 2014 - 11:47 PM
-snip-
I am trying to update this. It seems too much trouble doing it this way, I'll just write out the apis I'm going to include. This discussion is closed unless someone finds an easy to understand way of doing things.
CometWolf #24
Posted 14 May 2014 - 11:50 PM
if you want to execute code via rednet, you can just use loadstring

local id,message = rednet.receive()
rednet.send(id,loadstring(message)())
Edited on 14 May 2014 - 09:50 PM
awsmazinggenius #25
Posted 15 May 2014 - 01:15 AM
Or a table with a function. Modems have table capabilities (sans-serialization) these days, by the way.