Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local t={"CoolisOS",true,"CoolisOS/bin",true,"CoolisOS/bin/kernel",true,"CoolisOS/bin/kernel/compat.lua",[===================[--compat
- local interface_events={
- 'mouse_click',
- 'mouse_scroll',
- 'mouse_drag',
- 'monitor_touch',
- 'key',
- 'char',
- }
- --needs to determine the terminal in some other way.
- local check=require'checker'.check
- local compat={}
- function compat.get_pullEventRaw(term_em,plat_em)
- return function (filter)
- check('?string',filter)
- local t=assert(sched.running)
- if not t._native then
- local pipe=sched.pipe()
- t._native=pipe
- local h=sched.sighook(function(em,ev,...)
- if plat_em and em==plat_em and interface_events[ev] then
- return
- else
- pipe:send{ev,...}
- end
- end)
- if plat_em then h:link(plat_em,'*') end
- if term_em then h:link{[term_em]=interface_events} end
- end
- local pipe=t._native
- repeat
- local r=pipe:receive()
- until (filter==nil or filter==r[1])
- return unpack(r)
- end
- end
- function compat.adapt_env(env,term_em,plat_em)
- env.os.pullEventRaw=compat.get_pullEventRaw(term_em,plat_em)
- end
- function compat.adapt_global(_Gnative)
- local Box=require'kernel.class.Box'
- local turtle=_Gnative.turtle
- if turtle then
- local waitForResponse=function(id)
- repeat
- local em,ev,rid,p1=sched.wait(sched.platform,'turtle_response')
- if em==sched.platform then
- if id==rid then
- return p1
- end
- else return false,'timer' end
- until false
- end
- local native=turtle.native or turtle
- local turtle={}
- rawset(_Gnative,'turtle',turtle)
- turtle["getItemCount"] = native.getItemCount
- turtle["getItemSpace"] = native.getItemSpace
- turtle["getFuelLevel"] = native.getFuelLevel
- local function wrap( _sCommand )
- return function( ... )
- local id = native[_sCommand]( ... )
- if id == -1 then
- return false
- end
- return waitForResponse( id )
- end
- end
- for k,v in pairs( native ) do
- if type( k ) == "string" and type( v ) == "function" then
- if turtle[k] == nil then
- turtle[k] = wrap( k )
- end
- end
- end
- if peripheral.getType("left") == "workbench" then
- turtle["craft"] = function( ... )
- local id = peripheral.call( "left", "craft", ... )
- return waitForResponse( id )
- end
- elseif peripheral.getType("right") == "workbench" then
- turtle["craft"] = function( ... )
- local id = peripheral.call( "right", "craft", ... )
- return waitForResponse( id )
- end
- end
- end
- rednet.receive=function( nTimeout )
- local em,e, p1, p2, p3 = sched.wait(sched.platform,'rednet_message',nTimeout)
- if em==sched.platform then
- return p1, p2, p3
- else
- return
- end
- end
- local dyn_k={
- 'term',
- 'os',
- 'shell',
- }
- local dyn_f={
- 'write',
- 'printError',
- 'read',
- }
- for _,k in pairs(dyn_f) do
- rawset(_Gnative,k,function(...)
- local box=assert(Box.get_current(1))
- return box.env[k](...)
- end)
- end
- setmetatable(_Gnative,{
- __index=function(t,k)
- if k=='_BOX' then
- return Box.get_current(1)
- elseif dyn_k[k] then
- local box=assert(Box.get_current(1))
- return box.env[k]
- else return rawget(t,k) end
- end
- })
- end
- return compat]===================],"CoolisOS/bin/kernel/debug.lua",[===================[--debug
- include'kernel.class'
- local check=require'kernel.checker'.check
- local debug={}
- local function wrap(text, limit) --from Kingdaroo
- local lines = {''}
- for word, space in text:gmatch('(%S+)(%s*)') do
- local temp = lines[#lines] .. word .. space:gsub('\n','')
- if #temp > limit then
- table.insert(lines, '')
- end
- if space:find('\n') then
- lines[#lines] = lines[#lines] .. word
- space = space:gsub('\n', function()
- table.insert(lines, '')
- return ''
- end)
- else
- lines[#lines] = lines[#lines] .. word .. space
- end
- end
- return lines
- end
- function debug.step_print(s,t)
- check('string,Terminal',s,t)
- local x,y=t.term:getSize()
- y=y-1
- local tt=wrap(s,x)
- local r=#tt%y
- local n=(#tt-r)/y
- for i=0,n-1 do
- if i~=0 then t:write('\n') end
- t:write(table.concat(tt,'\n',i*y+1,(i+1)*y))
- t:write('\nPress any key to continue')
- repeat until (t.pin:receive())[2]=='char'
- if i==n-1 then t:write('\n') end
- end
- t:write(table.concat(tt,'\n',n*y+1,n*y+r))
- t:write('\n')
- end
- function debug.printError(s,t)
- check('string,Terminal',s,t)
- if t.term:isColour() then
- t.term:setTextColour( colours.red )
- end
- debug.step_print(s,t)
- t.term:setTextColour( colours.white )
- end
- --debug.traceback
- do
- --[[ Notes:
- getfenv(1)=getfenv()~=getfenv(0)~~_G
- error('',1)~=error('',0)
- ]]
- rawset(_G,'loadfile',function( _sFile )
- local file = fs.open( _sFile, "r" )
- if file then
- local func, err = loadstring( file.readAll(),_sFile)
- file.close()
- return func, err
- end
- return nil, "File not found"
- end)
- local traceback=function(level,terr,maxlevel)
- local terr=terr or {}
- local passed={}
- local err,ok=terr
- local j=2+(level or 0)
- repeat
- j=j+1
- passed[err]=true
- ok,err=pcall(error,'',j)
- --if err:match('^[^:]+')=='bios' then break end
- table.insert(terr,err)
- until (passed[err] or (maxlevel and (j+1-2>maxlevel) ) )
- return terr
- end
- local function classify(t,f)
- local bins={}
- local c
- for i,v in ipairs(t) do
- local fi,r=f(v)
- if fi~=nil then
- if c==nil or c.n~=fi then
- table.insert(bins,c)
- c={n=fi}
- end
- table.insert(c,r)
- end
- end
- table.insert(bins,c)
- return bins
- end
- local function get_chunks(terr)
- return classify(terr,function(v) return v:match('^([^:]+):(.*)') end)
- end
- local function get_lines(terr)
- return classify(terr,function(v) return v:match('^(%d+):(.*)') end)
- end
- local function put_lines(path,diameter,ts,l)
- local ok,f=pcall(fs.open,path,'r')
- if not ok or not f then table.insert(ts,' (could not open file)')
- else
- for i=1,l-diameter-1 do
- f.readLine()
- end
- for i=1,diameter do
- table.insert(ts,' '..f.readLine())
- end
- table.insert(ts,'->'..f.readLine())
- for i=1,diameter do
- local l=f.readLine()
- if l then table.insert(ts,' '..l)
- else break end
- end
- end
- end
- local function format_traceback(terr)
- local ts={}
- for i,v in ipairs(get_chunks(terr)) do
- local name=v.n or '(no name)'
- local path
- if fs.exists(name) then
- name,dir,path=fs.getName(name),name:match('^(.*)/') or '/',name
- name=name..' ('..(dir or 'unknown')..')'
- elseif false then
- --shell has input string; modify to behave as file?
- end
- table.insert(ts,name)
- for j,line in ipairs(get_lines(v)) do
- table.insert(ts,' line '..(line.n or '(no line)')..', *'..(#line))
- local n=line.n and tonumber(line.n) or 1
- if path and n then
- put_lines(path,1,ts,n)
- end
- end
- end
- return ts
- end
- function debug.traceback(msg,level,maxlevel)
- local level=level or 0
- local ts=format_traceback(traceback(level+1,{},maxlevel))
- msg=(msg and '\n' or '')..'['..(msg or 'debug.traceback')
- table.insert(ts,1,msg)
- return table.concat(ts,'\n')..'\n]'
- end
- function debug.printTraceback(msg,level)
- debug.printError(debug.traceback(msg,(level or 0)+1))
- end
- function debug.tracebackError(msg,level)
- error(debug.traceback(msg,(level or 0)+1),2)
- end
- end
- function debug.raw(t)
- if type(t)=='table' then
- local mt=getmetatable(t)
- if mt then
- local f=rawget(mt,'__tostring')
- rawset(mt,'__tostring',nil)
- local s=tostring(t)
- rawset(mt,'__tostring',f)
- return s
- else
- return tostring(t)
- end
- else
- return tostring(t)
- end
- end
- function debug.log(...)
- return log('debug','DEBUG','(%s):\nMsg:\n\t%s\nFrom:%s',sched.running or 'nil',debug.args_tostring(...),debug.traceback('',1,2))
- end
- function debug.objinfo(s)
- local name=debug.named[s]
- return (name and name..'|' or '')
- ..(typeof(s)=='object' and (classname(s) or debug.raw(s))..' instance|' or '')
- ..debug.raw(s) or ''
- end
- function debug.getinfo(v)
- if type(v)=='table' then
- return debug.objinfo(v)
- else
- return type(v)..':'..tostring(v)
- end
- end
- function debug.args_tostring(...)
- local args = {}
- local t = {...}
- for k = 1, table.maxn(t) do table.insert(args, (debug.getinfo)(t[k])) end
- return table.concat(args,',')
- end
- function debug.wrap(f,silent)
- return function(...)
- --wrapped level
- local r={xpcall(f,debug.traceback)}
- local ok=table.remove(r,1)
- if not ok then
- local er=r[1]
- if silent then return nil,er
- else error(er,2) end
- end
- return unpack(r)
- end
- end
- debug.named=setmetatable({},{__mode='kv'})
- function debug.name(obj,name)
- debug.named[obj]=name
- return obj
- end
- function debug.namefield(self,k,v)
- v=v==nil and self[k] or v
- check('table,!nil,!nil',self,k,v)
- self[k]=v
- debug.name(v,('(%s).(%s)'):format(debug.getinfo(self),debug.getinfo(k)))
- return v
- end
- debug.say=function(s) return log('init','INFO','task:(%s) says:%s',tostring(sched.running),s) end
- return debug]===================],"CoolisOS/bin/kernel/driver.lua",[===================[local driver={}
- local peripheral=peripheral
- function driver.update()
- for _, side in ipairs(rs.getSides()) do
- if peripheral.isPresent(side) then
- driver[side]=peripheral.wrap(side)
- -- sched.signal(peripheral,peripheral[side].getType(),side)
- else
- driver[side]=nil
- end
- end
- end
- return driver]===================],"CoolisOS/bin/kernel/init.lua",[===================[local path=shell.getRunningProgram()
- local function override(f)
- local t={}
- local fo=printError
- local key='printError'
- local co=coroutine.status
- local wrapped=function(s)
- coroutine.status=co
- for i,v in pairs(t) do
- rawset(v,key,fo)
- end
- local ok,err=pcall(f)
- if not ok then
- fo(err)
- end
- coroutine.yield()
- coroutine.yield()
- end
- for i=1,20 do
- local ok,err=pcall(getfenv,i)
- if ok then
- if rawget(err,key)~=nil and not t[err] then
- t[i]=err
- rawset(err,key,wrapped)
- end
- end
- end
- coroutine.status=nil
- repeat
- os.pullEvent()
- until false
- end
- local function fixes()
- local string=string
- local rawset, rawget,type=rawset, rawget,type
- local nativegetmetatable=getmetatable
- local s_meta=getfenv(('').sub)
- rawset(_G,'getmetatable',nil)
- local function getmetatable(t)
- if type(t)=='string' then
- return s_meta
- else
- return nativegetmetatable(t)
- end
- end
- rawset(_G,'getmetatable',getmetatable)
- local nativesetmetatable = setmetatable
- rawset(_G,'setmetatable',nil)
- local function setmetatable( _o, _t )
- if _t and type(_t) == "table" then
- local idx = rawget( _t, "__index" )
- local newidx=rawget( _t, "__newindex" )
- rawset(_t,"__index",nil)
- rawset(_t,"__newindex",nil)
- local r= nativesetmetatable( _o, _t )
- rawset(_t,"__index",idx)
- rawset(_t,"__newindex",newidx)
- local meta=getmetatable(_o)
- rawset(meta,"__index",idx)
- rawset(meta,"__newindex",newidx)
- return r
- else
- return nativesetmetatable( _o, _t )
- end
- end
- rawset(_G,'setmetatable',setmetatable)
- rawset(_G,'loadfile',function( _sFile )
- local file = fs.open( _sFile, "r" )
- if file then
- local func, err = loadstring( file.readAll(),_sFile)
- file.close()
- return func, err
- end
- return nil, "File not found"
- end)
- end
- local function start()
- debug.name(sched.running,'shell')
- --log.setLevel('sched','DEBUG')
- log.setLevel('DEBUG','debug')
- --log.setLevel('DEBUG','pipe')
- --log.setLevel('ALL')
- --log.store.instant=true
- require'kernel.compat'.adapt_global(_G)
- local gui=require'kernel.gui'
- local terminal=gui.Terminal.from_native(_G.term,sched.platform)
- local shell=require'kernel.gui.shell'
- local Box=require'kernel.class.Box'
- Box{pin=terminal.pin,pout=terminal.pout,terminal=terminal}:run(
- function()
- local s=shell.new()
- s.run_startups()
- end)
- sched.wait('end')
- end
- local main=function()
- --save copy of old _G
- rawset(_G,'_Gstore',{
- old=(function()
- local c={}
- for i,v in pairs(_G) do
- c[i]=v
- end
- return c
- end)(),
- base=_G,
- base_env=getfenv(),
- })
- --overrides
- fixes()
- ---set install variables and module loading utilities
- do
- _FILE_PATH=path
- local loc=(_FILE_PATH:match'(.*)bin/kernel/init%.lua'):sub(1,-2)
- rawset(_G,'_INSTALL_PATH',loc)
- rawset(_G,'loadreq',dofile(fs.combine(_INSTALL_PATH,'bin/kernel/loadreq/init.lua')))
- rawset(_G,'require',loadreq.require)
- rawset(_G,'include',loadreq.include)
- loadreq.vars.paths=loadreq.vars.paths:gsub('%?',fs.combine(_INSTALL_PATH,'bin')..'/%?')..';'..loadreq.vars.paths..';'..loadreq.vars.paths:gsub('%?',_INSTALL_PATH..'/%?')
- end
- ---load modules
- do
- local function G(k,v)
- rawset(_G,k,v)
- end
- G('util',require'kernel.util')
- G('log',require'kernel.log')
- --log.store=require'kernel.log.store'
- --log.store.reset()
- do
- local serpent=require'utils.serpent'
- G('pstring',function(this,max_level) return serpent.serialize(this,{debug=true,indent = ' ', sortkeys = true, comment = true,maxlevel=max_level}) end)
- G('pprint',function(this,max_level)
- debug.step_print(pstring(this),getfenv(2).term)
- end)
- G('debug',require'kernel.debug')
- G('log',require'kernel.log')
- local globals={
- -- read=require'utils.tab_read',
- net=require'kernel.net',
- sched=require'kernel.sched',
- }
- for i,v in pairs(globals) do G(i,v) end
- for i,v in pairs(require'kernel.class') do
- G(i,v)
- end
- end
- --overrided to os
- do
- os.sleep(0)
- local o_os=os
- local check=require'kernel.checker'.check
- local os=util.shcopy(o_os)
- local function wrap(f,i,e)
- return function(...)
- local ir,fr=nil,nil
- if i then ir={i(...)} end
- fr={f(...)}
- if e then e(fr,ir) end
- return unpack(fr)
- end
- end
- os.version=function()
- return 'CoolisOS.1'
- end
- os.sleep=function(n)
- if sched.running then
- check('number',n) sched.wait(n)
- else return o_os.sleep(n) end
- end
- rawset(_G,'sleep',os.sleep)
- rawset(_G,'os',os)
- end
- end
- sched.task(function()
- debug.wrap(start)()
- end):run()
- sched.loop()
- log.store.flush()
- print'Press any key to shutdown'
- sleep(2)
- os.pullEvent'key'
- os.shutdown()
- end
- override(function()
- local ok,er=xpcall(main,function(msg,level)
- if _G.debug then
- return _G.debug.traceback(msg)
- else
- return msg
- end
- end)
- if not ok then
- if log then
- log('init','ERROR','%s',er)
- error(er)
- end
- end
- end)]===================],"CoolisOS/bin/kernel/net.lua",[===================[local string_mt=getfenv(('').len)
- local net=string_mt.__net or {}
- string_mt.__net=net
- net.ids=net.ids or setmetatable({},{__mode='v'})
- local ids=net.ids
- local id=os.getComputerID()
- ids[id]=_G
- net.queueEvent=function(id,...)
- if net.ids[id] then
- ok,err=pcall(net.ids[id].os.queueEvent,...)
- if not ok then
- net.ids[id]=nil
- return
- end
- return true
- end
- end
- net.queueAll=function(...)
- local t={}
- for id in pairs(net.ids) do
- t[id]=net.queueEvent(id,...)
- end
- return t
- end
- net.rednet={
- send=function(id,msg)
- return net.queueEvent(id,'rednet_message',os.getComputerID(),msg,nil)
- end,
- broadcast=function(id,msg)
- return net.queueAll('rednet_message',os.getComputerID(),msg,nil)
- end,
- }
- return net]===================],"CoolisOS/bin/kernel/util.lua",[===================[--- ## Moses: <em>A utility-belt library for functional programming in Lua.</em><br/>
- -- @author Roland_Yonaba
- -- @copyright 2012
- -- @license [MIT](http://www.opensource.org/licenses/mit-license.php)
- -- @script moses
- -- Internalisation
- local error,pcall=error,pcall
- local next, type, unpack, select = next, type, unpack, select
- local setmetatable, getmetatable = setmetatable, getmetatable
- local t_insert, t_sort = table.insert, table.sort
- local t_remove,t_concat = table.remove, table.concat
- local randomseed, random, huge = math.randomseed, math.random, math.huge
- local floor, max, min = math.floor, math.max, math.min
- local getfenv = getfenv
- local unpack = unpack
- local pairs,ipairs = pairs,ipairs
- local _ = {}
- -- ======== Private helpers
- local function f_max(a,b) return a>b end
- local function f_min(a,b) return a<b end
- local function clamp(var,a,b) return (var<a) and a or (var>b and b or var) end
- local function isTrue(_,value) return value and true end
- local function iNot(value) return not value end
- local function count(t)
- local i
- for k,v in pairs(t) do i = (i or 0) + 1 end
- return i
- end
- local function extract(list,comp,transform,...)
- local _ans
- local transform = transform or _.identity
- for index,value in pairs(list) do
- if not _ans then _ans = transform(value,...)
- else
- local value = transform(value,...)
- _ans = comp(_ans,value) and _ans or value
- end
- end
- return _ans
- end
- -- Internal counter for unique ids generation
- local unique_id_counter = -1
- -- ========= Math utilities
- function _.format_number(n,r) local e=10^r local b=n%1 local a=n-b local c=b*e local c=c-c%1 return a+c/e end
- -- ========= Filesystem utilities
- function _.with(handle,f,...)
- local ok, err=pcall(f,handle,...)
- if handle and handle.close then handle:close() end
- if not ok then error('util.with:'..err,2) end
- return ok,err
- end
- -- ========== Table utilities
- function _.set(t,...)
- local arg={...}
- local t,n=t,#arg
- for i=1,(n-2) do
- t[arg[i]]=t[arg[i]] or {}
- t=t[arg[i]]
- end
- t[arg[n-1]]=arg[n]
- end
- function _.get(t,...)
- local arg={...}
- for i=1,#arg do
- t=t[arg[i]]
- if t==nil then return nil end
- end
- end
- function _.delete(t,...)
- local arg={...}
- local n=#arg
- for i=1,n-1 do
- t=t[arg[i]]
- if t==nil then return nil end
- end
- t[arg[n]]=nil
- end
- function _.map_args(t,f,...)
- local args = {}
- for k = 1, table.maxn(t) do table.insert(args,f(t[k],...)) end
- return args
- end
- -- ========= Scheduler utilities
- function _.wait(f,match,...)
- local r,b
- repeat
- r={f(...)}
- b=true
- for i,v in pairs(match) do
- if r[i]~=nil and r[i]~=v then
- b=false
- break
- end
- end
- until b
- return r
- end
- -- ========= Table functions
- --------------------------------------------------------------------------------
- ---nils a table of arbitrary keys/values
- -- @function [parent=#utils.table] tnil
- -- @param table to nil
- function _.tnil(t)
- local del=next(t)
- if del then
- for i,v in next,t,del do
- t[del]=nil
- del=i
- end
- t[del]=nil
- end
- end
- function _.shcopy(src, dst)
- dst = dst or {}
- for k, v in pairs(src) do
- dst[k]=v
- end
- return dst
- end
- --------------------------------------------------------------------------------
- -- Copies a table from the source to destination table.
- --
- -- The copy is recursive.
- --
- -- @function [parent=#utils.table] copy
- -- @param src a table to be used as the source of the copy.
- -- @param dst a table to be used as the destination of the copy.
- -- @param overwrite boolean value, to enable overwrite of existing field in dst table.
- -- @return the resulting dst table.
- --
- local _copy
- _copy=function(passed,src, dst, overwrite)
- dst = dst or {}
- passed[src]=dst
- for k, v in pairs(src) do
- if type(v) == 'table' then -- recursively go into tables
- if passed[v] then
- dst[k]=passed[v]
- else
- if not (type(dst[k]) == 'table') and (overwrite or dst[k]==nil) then
- dst[k] = {}
- end
- _copy(passed,v, dst[k], overwrite)
- end
- elseif overwrite or dst[k]==nil then dst[k] = v end
- end
- return dst
- end
- _.copy=function(...) return _copy({},...) end
- --------------------------------------------------------------------------------
- -- Produces a diff of two tables.
- --
- -- Recursive diff by default.
- --
- -- @function [parent=#utils.table] diff
- -- @param t1 first table to compare.
- -- @param t2 second table to compare.
- -- @param norecurse boolean value to disable recursive diff.
- -- @return the diff result as a table.
- -- @usage
- --> t1 = { a = 1, b=3, c = "foo"}
- --> t2 = { a = 1, b=3, c = "foo"}
- --> :diff(t1, t2)
- --= { }
- --> t1 = { a = 1, b=3, c = "foo"}
- --> t2 = { a = 1, b=4, c = "foo"}
- --> :diff(t1, t2)
- --= { "b" }
- --> t1 = { a = 1, b=3, c = "foo"}
- --> t2 = { a = 1, b=3}
- --> :diff(t1, t2)
- --= { "c" }
- --> t1 = { b=3, c = "foo", d="bar"}
- --> t2 = { a = 1, b=3, c = "foo"}
- --> :diff(t1, t2)
- --= {
- -- "a",
- -- "d" }
- --
- function _.diff(t1, t2, norecurse)
- local d = {}
- local t3 = {}
- local rpairs = norecurse and pairs or _.recursivepairs
- for k, v in rpairs(t1) do t3[k] = v end
- for k, v in rpairs(t2) do
- if v ~= t3[k] then table.insert(d, k) end
- t3[k] = nil
- end
- for k, v in pairs(t3) do table.insert(d, k) end
- return d
- end
- --- Iterators
- -- @section It
- --------------------------------------------------------------------------------
- -- Iterator that iterates on the table in key ascending order.
- --
- -- @function [parent=#utils.table] sortedPairs
- -- @param t table to iterate.
- -- @return iterator function.
- --
- function _.sortedpairs(t)
- local a = {}
- local insert = table.insert
- for n in pairs(t) do insert(a, n) end
- table.sort(a)
- local i = 0
- return function()
- i = i + 1
- return a[i], t[a[i]]
- end
- end
- --------------------------------------------------------------------------------
- -- Iterator that returns key/value pairs except walking through sub tables and concatenating the path in the key.
- --
- -- This iterator break cycles: it does not recur. If it detects a cycle, the entry causing a cycle is ignored.
- -- However, if a table is repeated, its contents will be repeated by this function.
- --
- -- @function [parent=#utils.table] recursivePairs
- -- @param t table to iterate.
- -- @param prefix path prefix to prepend to the key path returned.
- -- @return iterator function.
- -- @usage {toto={titi=1, tutu=2}, tata = 3, tonton={4, 5}} will iterate through
- -- ("toto.titi",1), ("toto.tutu",2), ("tata",3) ("tonton.1", 4), ("tonton.2"=5)
- --
- function _.recursivepairs(t, prefix)
- check('table,?|string',t, prefix)
- local function it(t, prefix, cp)
- cp[t] = true
- local pp = prefix == "" and prefix or "."
- for k, v in pairs(t) do
- k = pp..tostring(k)
- if type(v) == 'table' then
- if not cp[v] then it(v, prefix..k, cp) end
- else
- coroutine.yield(prefix..k, v)
- end
- end
- cp[t] = nil
- end
- prefix = prefix or ""
- return coroutine.wrap(function() it(t, cleanpath(prefix), {}) end)
- end
- --------------------------------------------------------------------------------
- -- Iterator that returns key/value on multiple tables.
- --
- -- @function [parent=#utils.map] multiPairs
- -- Tables are traversed in the same order as they are passed as parameters.
- -- @param t1 first table to traverse.
- -- @param ... additional tables to traverse.
- -- @return iterator function.
- --
- function _.multipairs(t1, ...)
- local state = { tables = {t1, ...}, i=1, k=next(t1) }
- local function f()
- if not state.i then return end
- local current_table = state.tables[state.i]
- local k, v = state.k, current_table[state.k]
- local next_k = next(current_table, k)
- while next_k==nil do
- state.i = state.i+1
- local next_table = state.tables [state.i]
- if not next_table then state.i=false; break
- else next_k = next (next_table) end
- end
- state.k = next_k
- return k, current_table[k]
- end
- return f, nil, nil
- end
- -- ========= Collection functions
- --- Calls function `f` on each key-value of a given collection. <br/><em>Aliased as @{forEach}</em>.
- -- @name each
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @see forEach
- function _.each(list, f, ...)
- if not _.isObject(list) then return end
- for index,value in pairs(list) do
- f(index,value,...)
- end
- return list
- end
- --- Alias for @{each}.
- -- @function forEach
- -- @param list
- -- @param f
- -- @param[opt] ...
- _.forEach = _.each
- --- Maps function `f` on each key-value of a given collection. Collects
- -- and returns the outputs.<br/><em>Aliased as @{collect}</em>.
- -- @name map
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @treturn table a table of results
- -- @see collect
- function _.map(list, f, ...)
- local _list = {}
- for index,value in pairs(list) do
- _list[index] = f(index,value,...)
- end
- return _list
- end
- --- Alias for @{map}.
- -- @function collect
- -- @param list
- -- @param f
- -- @param[opt] ...
- _.collect = _.map
- --- Reduces an entire collection. Folds from left to right to a single value,
- -- with respect to a given iterator and an initial state.
- -- The given function takes a state and a value, and returns a new state.
- -- <br/><em>Aliased as @{inject}, @{foldl}</em>.
- -- @name reduce
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(state,value)`
- -- @tparam[opt] state state an initial state of reduction. Defaults to the first value in the list.
- -- @treturn state state a final state of reduction
- -- @see inject
- -- @see foldl
- function _.reduce(list,f,state)
- for _,value in pairs(list) do
- state = not state and value or f(state,value)
- end
- return state
- end
- --- Alias for @{reduce}.
- -- @function inject
- -- @param list
- -- @param f
- -- @param[opt] state
- _.inject = _.reduce
- --- Alias for @{reduce}.
- -- @function foldl
- -- @param list
- -- @param f
- -- @param[opt] state
- _.foldl = _.reduce
- --- Reduces an entire collection. Folds from right to left to a single value,
- -- with respect to a given iterator and an initial state.
- -- The given function takes a state and a value, and returns a new state.
- -- <br/><em>Aliased as @{injectr}, @{foldr}</em>.
- -- @name reduceRight
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(state,value)`
- -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the list.
- -- @treturn state state a final state of reduction
- -- @see injectr
- -- @see foldr
- function _.reduceRight(list,f,state)
- return _.reduce(_.reverse(list),f,state)
- end
- --- Alias for @{reduceRight}.
- -- @function injectr
- -- @param list
- -- @param f
- -- @param[opt] state
- _.injectr = _.reduceRight
- --- Alias for @{reduceRight}.
- -- @function foldr
- -- @param list
- -- @param f
- -- @param[opt] state
- _.foldr = _.reduceRight
- --- Reduces a collection while saving intermediate states.
- -- Folds the collection from left to right to a single value,
- -- with respect to a given iterator and an initial state.
- -- The given function takes a state and a value, and returns a new state.
- -- The function returns an array of intermediate states.
- -- <br/><em>Aliased as @{mapr}</em>
- -- @name mapReduce
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(state,value)`
- -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the list.
- -- @treturn table an array of states
- -- @see mapr
- function _.mapReduce(list,f,state)
- local t = {}
- for i,value in pairs(list) do
- t[i] = not state and value or f(state,value)
- state = t[i]
- end
- return t
- end
- --- Alias for @{mapReduce}.
- -- @function mapr
- -- @param list
- -- @param f
- -- @param[opt] state
- _.mapr = _.mapReduce
- --- Reduces a collection while saving intermediate states. It proceeds in reverse order,
- -- folding the collection from right to left to a single value,
- -- with respect to a given iterator and an initial state.
- -- The given function takes a state and a value, and returns a new state.
- -- The function returns an array of intermediate states.
- -- <br/><em>Aliased as @{maprr}</em>
- -- @name mapReduceRight
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(state,value)`
- -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the list.
- -- @treturn table an array of states
- -- @see maprr
- function _.mapReduceRight(list,f,state)
- return _.mapReduce(_.reverse(list),f,state)
- end
- --- Alias for @{mapReduceRight}.
- -- @function maprr
- -- @param list
- -- @param f
- -- @param[opt] state
- _.maprr = _.mapReduceRight
- --- Looks for an object in a collection. <br/><em>Aliased as @{any}, @{some}</em>
- -- @name include
- -- @tparam table list a collection
- -- @tparam object item a value to be searched
- -- @treturn boolean a boolean : __true__ when found, __false__ otherwise
- -- @see any
- -- @see some
- function _.include(list,item)
- local _iter = _.isFunction(item) and item or _.isEqual
- for _,value in pairs(list) do
- if _iter(value,item) then return true end
- end
- return false
- end
- --- Alias for @{include}.
- -- @function any
- -- @param list
- -- @param item
- _.any = _.include
- --- Alias for @{include}.
- -- @function some
- -- @param list
- -- @param item
- _.some = _.include
- --- Looks for an object index in a collection. <br/><em>Aliased as @{where}, @{find}</em>
- -- @name detect
- -- @tparam table list a collection
- -- @tparam object item a value to be searched
- -- @treturn key the object key or __nil__
- -- @see find
- -- @see where
- function _.detect(list,item)
- local _iter = _.isFunction(item) and item or _.isEqual
- for key,arg in pairs(list) do
- if _iter(arg,item) then return key end
- end
- end
- --- Alias for @{detect}.
- -- @function find
- -- @param list
- -- @param item
- _.find = _.detect
- --- Alias for @{detect}.
- -- @function where
- -- @param list
- -- @param item
- _.where = _.detect
- --- Selects and extracts objects passing an iterator test.
- -- <br/><em>Aliased as @{filter}</em>
- -- @name select
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @treturn table the selected objects
- -- @see filter
- function _.select(list,f,...)
- local _mapped = _.map(list,f,...)
- local _list = {}
- for index,value in pairs(_mapped) do
- if value then _list[#_list+1] = list[index] end
- end
- return _list
- end
- --- Alias for @{select}.
- -- @function filter
- -- @param list
- -- @param f
- -- @param[opt] ...
- _.filter = _.select
- --- Clones a collection, rejecting objects passing an iterator test.
- -- <br/><em>Aliased as @{discard}</em>
- -- @name reject
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @treturn table the remaining objects
- -- @see discard
- function _.reject(list,f,...)
- local _mapped = _.map(list,f,...)
- local _list = {}
- for index,value in pairs (_mapped) do
- if not value then _list[#_list+1] = list[index] end
- end
- return _list
- end
- --- Alias for @{reject}.
- -- @function discard
- -- @param list
- -- @param f
- -- @param[opt] ...
- _.discard = _.reject
- --- Tests if all objects in a collection passes an iterator test. <br/><em>Aliased as @{every}</em>
- -- @name all
- -- @tparam table list a collection
- -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @treturn boolean __true__ or __false__
- -- @see every
- function _.all(list,f,...)
- return ((#_.select(_.map(list,f,...), isTrue)) == (#list))
- end
- --- Alias for @{all}.
- -- @function every
- -- @param list
- -- @param f
- -- @param[opt] ...
- _.every = _.all
- --- Invokes a method on each object in a collection.
- -- @name invoke
- -- @tparam table list a collection
- -- @tparam function method a function, prototyped as `f(object,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `method`
- -- @treturn table an array of results
- function _.invoke(list,method,...)
- local args = {...}
- return _.map(list, function(__,v)
- if _.isObject(v) then
- if _.has(v,method) then
- if _.isCallable(v[method]) then
- return v[method](v,unpack(args))
- else return v[method]
- end
- else
- if _.isCallable(method) then
- return method(v,unpack(args))
- end
- end
- elseif _.isCallable(method) then
- return method(v,unpack(args))
- end
- end)
- end
- --- Extracts property-values from a collection of objects.
- -- @name pluck
- -- @tparam table list a collection
- -- @tparam string a property, to index in each object: `object[property]`
- -- @treturn table an array of values for the specified property
- function _.pluck(list,property)
- return _.reject(_.map(list,function(__,value)
- return value[property]
- end),
- iNot)
- end
- --- Returns the maximum value in a collection. If an iterator is passed, it will
- -- be used to extract the property value by which all objects will be ranked.
- -- @name max
- -- @tparam table list a collection
- -- @tparam[opt] function iter an iterator function, prototyped as `iter(value,...)`, defaults to @{identity}
- -- @tparam[optchain] var_arg ... Optional extra-args to be passed to function `iter`
- -- @treturn value the maximum property value found
- function _.max(list,iter,...)
- return extract(list,f_max,iter,...)
- end
- --- Returns the minimum value in a collection. If an iterator is passed, it will
- -- be used to extract the property value by which all objects will be ranked.
- -- @name min
- -- @tparam table list a collection
- -- @tparam[opt] function iter an iterator function, prototyped as `iter(value,...)`, defaults to @{identity}
- -- @tparam[optchain] var_arg ... Optional extra-args to be passed to function `iter`
- -- @treturn value the minimum property value found
- function _.min(list, iter,...)
- return extract(list,f_min, iter,...)
- end
- --- Returns a shuffled copy of a given collection. If a seed is provided, it will
- -- be used to init the random number generator.
- -- @name shuffle
- -- @tparam table list a collection
- -- @tparam[opt] number seed a seed
- -- @treturn table a shuffled copy of the given collection
- function _.shuffle(list,seed)
- if seed then randomseed(seed) end
- local _shuffled = {}
- _.each(list,function(index,value)
- local randPos = floor(random()*index)+1
- _shuffled[index] = _shuffled[randPos]
- _shuffled[randPos] = value
- end)
- return _shuffled
- end
- --- Tests if two collections are the same. I.e, they feature the same objects, but not
- -- necessarily at the same keys
- -- @name same
- -- @tparam table a a collection
- -- @tparam table b a collection
- -- @treturn boolean __true__ or __false__
- function _.same(a,b)
- return _.all(a, function (i,v)
- return _.include(b,v)
- end)
- and _.all(b, function (i,v)
- return _.include(a,v)
- end)
- end
- --- Sorts a collection. If a comparison function is given, it will be used to sort objects
- -- @name sort
- -- @tparam table list a collection
- -- @tparam[opt] function comp a comparison function prototyped as `comp(a,b)`, defaults to <tt><</tt> operator.
- -- @treturn table the given list, sorted.
- function _.sort(list,comp)
- t_sort(list,comp)
- return list
- end
- --- Converts a list of __var_args__ to a collection.
- -- @name toArray
- -- @tparam[opt] var_arg ... Optional variable number of arguments
- -- @treturn table an array of all passed-in args
- function _.toArray(...)
- return {...}
- end
- --- Splits a collection into subsets. Each subset feature objects grouped by the result of passing it through an iterator.
- -- If iterator is a string instead of a function, groups by the property named by iterator on each of the values.
- -- @name groupBy
- -- @tparam table list a collection
- -- @tparam function iter an iterator function, prototyped as `iter(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `iter`
- -- @treturn table a new collection with items grouped by subsets
- function _.groupBy(list,iter,...)
- local var_arg = {...}
- local _list = {}
- local _iter = _.isFunction(iter) and iter
- or (_.isString(iter) and function(_,item)
- return item[iter](item,unpack(var_arg))
- end)
- if not _iter then return end
- _.each(list, function(i,v)
- local _key = _iter(i,v)
- if _list[_key] then _list[_key][#_list[_key]+1] = v
- else _list[_key] = {v}
- end
- end)
- return _list
- end
- --- Groups objects in a collection and counts them.
- -- @name countBy
- -- @tparam table list a collection
- -- @tparam function iter an iterator function, prototyped as `iter(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `iter`
- -- @treturn table a new collection with subsets names and count
- function _.countBy(list,iter,...)
- local var_arg = {...}
- local stats = {}
- _.each(list,function(i,v)
- local key = iter(i,v,unpack(var_arg))
- stats[key] = (stats[key] or 0) +1
- end)
- return stats
- end
- --- Counts the number of values in a collection. If being passed more than one args
- -- it will return the number of passed-in args.
- -- @name size
- -- @tparam[opt] var_arg ... Optional variable number of arguments
- -- @treturn number a size
- function _.size(...)
- local args = {...}
- local arg1 = args[1]
- if _.isNil(arg1) then
- return 0
- elseif _.isObject(arg1) then
- return count(args[1])
- else
- return count(args)
- end
- end
- --- Checks if all the keys of `other` object exists in a `list`. It does not
- -- compares values. The test is not commutative, i.e `list` may contains keys
- -- not existing in `other`.
- -- @name containsKeys
- -- @tparam table list a collection
- -- @tparam table other a collection
- -- @treturn boolean __true__ or __false__
- function _.containsKeys(list,other)
- for key in pairs(other) do
- if not list[key] then return false end
- end
- return true
- end
- --- Checks if both given lists have the same keys. It does not compares values.
- -- @name sameKeys
- -- @tparam table listA a collection
- -- @tparam table listB a collection
- -- @treturn boolean __true__ or __false__
- function _.sameKeys(listA,listB)
- _.each(listA,function(key)
- if not listB[key] then return false end
- end)
- _.each(listB,function(key)
- if not listA[key] then return false end
- end)
- return true
- end
- -- ========= Array functions
- --- Reverses values in a given array. The passed-in array should not be sparse.
- -- @name reverse
- -- @tparam table array an array
- -- @treturn table a copy of the given array, reversed
- function _.reverse(array)
- local _array = {}
- for i = #array,1,-1 do
- _array[#_array+1] = array[i]
- end
- return _array
- end
- --- Collects values from a given array. The passed-in array should not be sparse.
- -- This function collects values as long as they satisfy a given predicate.
- -- Therefore, it returns on the first false test.
- -- <br/><em>Aliased as @{takeWhile}</em>
- -- @name selectWhile
- -- @tparam table array an array
- -- @tparam function f an iterator function prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @treturn table a new table containing the first truthy values collected
- -- @see takeWhile
- function _.selectWhile(array,f,...)
- local t = {}
- for i,v in ipairs(array) do
- if f(i,v,...) then t[i] = v else break end
- end
- return t
- end
- --- Alias for @{selectWhile}.
- -- @function takeWhile
- -- @param array
- -- @param f
- -- @param[opt] ...
- _.takeWhile = _.selectWhile
- --- Collects values from a given array. The passed-in array should not be sparse.
- -- This function collects values as soon as they do not satisfy a given predicate.
- -- Therefore it returns on the first true test.
- -- <br/><em>Aliased as @{rejectWhile}</em>
- -- @name dropWhile
- -- @tparam table array an array
- -- @tparam function f an iterator function prototyped as `f(key,value,...)`
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
- -- @treturn table a new table containing the first falsy values collected
- -- @see rejectWhile
- function _.dropWhile(array, f,...)
- local _i
- for i,v in ipairs(array) do
- if not f(i,v,...) then
- _i = i
- break
- end
- end
- if _.isNil(_i) then return {} end
- return _.rest(array,_i)
- end
- --- Alias for @{dropWhile}.
- -- @function rejectWhile
- -- @param array
- -- @param f
- -- @param[opt] ...
- _.rejectWhile = _.dropWhile
- --- Returns the index at which a value should be inserted. This returned index is determined so that it maintains the sort.
- -- If a comparison function is passed, it will be used to determine the sort ranking of each
- -- value, including the passed-in value.
- -- @name sortedIndex
- -- @tparam table array an array
- -- @tparam value the value to be inserted
- -- @tparam[opt] function comp an comparison function prototyped as `f(a,b)`, defaults to <tt><</tt> operator
- -- @tparam[optchain] boolean sort whether or not to sort the passed-in array
- -- @treturn index the index at which the passed-in value should be inserted
- function _.sortedIndex(array,value,comp,sort)
- local _comp = comp or f_min
- if sort then _.sort(array,_comp) end
- for i = 1,#array do
- if not _comp(array[i],value) then return i end
- end
- return #array+1
- end
- --- Returns the index of the given value in an array. If the passed-in value exists
- -- more than once in the array, it will return the index of the first occurence.
- -- @name indexOf
- -- @tparam table array an array
- -- @tparam value the value to be searched
- -- @treturn index the index of the passed-in value or __nil__
- function _.indexOf(array, value)
- for k = 1,#array do
- if array[k] == value then return k end
- end
- end
- --- Returns the last occurence index of a given value.
- -- @name lastIndexOf
- -- @tparam table array an array
- -- @tparam value the value to be searched
- -- @treturn index the index of the last occurence of the passed-in value or __nil__
- function _.lastIndexOf(array,value)
- local key = _.indexOf(_.reverse(array),value)
- if key then return #array-key+1 end
- end
- --- Adds all passed-in values at the top of an array. The latter args will come the
- -- first in the given array.
- -- @name add
- -- @tparam table array an array
- -- @tparam var_arg ... a variable number of arguments
- -- @treturn table the passed-in array
- function _.add(array,...)
- _.each({...},function(i,v) t_insert(array,1,v) end)
- return array
- end
- --- Pushes all passed-in values at the end of an array.
- -- @name push
- -- @tparam table array an array
- -- @tparam var_arg ... a variable number of arguments
- -- @treturn table the passed-in array
- function _.push(array,...)
- _.each({...}, function(i,v) array[#array+1] = v end)
- return array
- end
- --- Removes and returns the value at the top of a given array.
- -- <br/><em>Aliased as @{shift}</em>
- -- @name pop
- -- @tparam table array an array
- -- @treturn value the popped value
- -- @see shift
- function _.pop(array)
- local retValue = array[1]
- t_remove(array,1)
- return retValue
- end
- --- Alias for @{pop}.
- -- @function shift
- -- @param array
- _.shift = _.pop
- --- Removes and returns the value at the end of a given array.
- -- @name unshift
- -- @tparam table array an array
- -- @treturn value the popped value
- function _.unshift(array)
- local retValue = array[#array]
- t_remove(array)
- return retValue
- end
- --- Trims all values indexed within the range `[start,finish]`.
- -- <br/><em>Aliased as @{rmRange}</em>
- -- @name removeRange
- -- @tparam table array an array
- -- @tparam[opt] index start the lower bound index, defaults to the first index in the array.
- -- @tparam[optchain] index finish the upper bound index, defaults to the array length.
- -- @treturn table the passed-in array
- -- @see rmRange
- function _.removeRange(array,start,finish)
- local array = _.clone(array)
- local i,n = (next(array)),#array
- if n < 1 then return array end
- start = clamp(start or i,i,n)
- finish = clamp(finish or n,i,n)
- if finish < start then return array end
- local count = finish - start + 1
- local i = start
- while count > 0 do
- t_remove(array,i)
- count = count - 1
- end
- return array
- end
- --- Alias for @{removeRange}.
- -- @function rmRange
- -- @param array
- -- @param[opt] start
- -- @param[optchain] finish
- _.rmRange = _.removeRange
- --- Slices values indexed within `[start,finish]` range.
- -- @name slice
- -- @tparam table array an array
- -- @tparam[opt] index start the lower bound index, defaults to the first index in the array.
- -- @tparam[optchain] index finish the upper bound index, defaults to the array length.
- -- @treturn table a new array
- function _.slice(array,start,finish)
- return _.select(array, function(index)
- return (index >= (start or next(array)) and index <= (finish or #array))
- end)
- end
- --- Returns the first N values in an array. <br/><em>Aliased as @{head}, @{take}</em>
- -- @name first
- -- @tparam table array an array
- -- @tparam[opt] number n the number of values to be collected, defaults to 1.
- -- @treturn table a new array
- -- @see head
- -- @see take
- function _.first(array,n)
- local n = n or 1
- return _.slice(array,1, min(n,#array))
- end
- --- Alias for @{first}.
- -- @function head
- -- @param array
- -- @param[opt] n
- _.head = _.first
- --- Alias for @{first}.
- -- @function take
- -- @param array
- -- @param[opt] n
- _.take = _.first
- --- Returns all values in an array excluding the last N values.
- -- @name initial
- -- @tparam table array an array
- -- @tparam[opt] number n the number of values to be left, defaults to the array length.
- -- @treturn table a new array
- function _.initial(array,n)
- if n and n < 0 then return end
- return _.slice(array,1, n and #array-(min(n,#array)) or #array-1)
- end
- --- Returns the last N values in an array.
- -- @name last
- -- @tparam table array an array
- -- @tparam[opt] number n the number of values to be collected, defaults to the array length.
- -- @treturn table a new array
- function _.last(array,n)
- if n and n <= 0 then return end
- return _.slice(array,n and #array-min(n-1,#array-1) or 2,#array)
- end
- --- Trims all values before index. <br/><em>Aliased as @{tail}</em>
- -- @name rest
- -- @tparam table array an array
- -- @tparam[opt] index index an index, defaults to 1
- -- @treturn table a new array
- -- @see tail
- function _.rest(array,index)
- if index and index > #array then return {} end
- return _.slice(array,index and max(1,min(index,#array)) or 1,#array)
- end
- --- Alias for @{rest}.
- -- @function tail
- -- @param array
- -- @param[opt] index
- _.tail = _.rest
- --- Trims all falsy values.
- -- @name compact
- -- @tparam table array an array
- -- @treturn table a new array
- function _.compact(array)
- return _.reject(array, function (_,value)
- return not value
- end)
- end
- --- Flattens a nested array. Passing `shallow` will only flatten at the first single level.
- -- @name flatten
- -- @tparam table array an array
- -- @tparam[opt] boolean shallow specifies the flattening depth
- -- @treturn table a new array, flattened
- function _.flatten(array, shallow)
- local shallow = shallow or false
- local new_flattened
- local _flat = {}
- for key,value in pairs(array) do
- if _.isObject(value) and not shallow then
- new_flattened = _.flatten (value)
- _.each(new_flattened, function(_,item) _flat[#_flat+1] = item end)
- else _flat[#_flat+1] = value
- end
- end
- return _flat
- end
- --- Returns values from an array not present in all passed-in args. <br/><em>Aliased as @{without}</em>
- -- @name difference
- -- @tparam table array an array
- -- @tparam var_arg ... a variable number of arguments
- -- @treturn table a new array
- -- @see without
- function _.difference(array,...)
- local values = _.toArray(...)
- return _.select(array,function(i,value)
- return not _.include(values,value)
- end)
- end
- --- Alias for @{difference}.
- -- @function without
- -- @param array
- -- @param ...
- _.without = _.difference
- --- Produces a duplicate-free version of a given array. Passing `isSorted` will
- -- provide a much faster version, but will produce the desired result if the array
- -- is properly sorted. If `iter` is passed, it will be used to compute new values based on
- -- a transformation.
- -- <br/><em>Aliased as @{unique}</em>
- -- @name uniq
- -- @tparam table array an array
- -- @tparam[opt] boolean isSorted whether or not the passed-in array is already sorted, defaults to `false`
- -- @tparam[optchain] function iter an iterator function prototyped as `iter(key,value,...)`
- -- @tparam[optchain] var_arg ... Optional extra-args to be passed to `iter` function
- -- @treturn table a new array
- -- @see unique
- function _.uniq(array,isSorted,iter,...)
- local init = iter and _.map(array,iter,...) or array
- local result = {}
- if not isSorted then
- for __,v in ipairs(init) do
- if not _.include(result,v) then
- result[#result+1] = v
- end
- end
- return result
- end
- result[#result+1] = init[1]
- for i = 2,#init do
- if init[i] ~= result[#result] then
- result[#result+1] = init[i]
- end
- end
- return result
- end
- --- Alias for @{uniq}.
- -- @function unique
- -- @param array
- -- @param[opt] isSorted
- -- @param[optchain] iter
- -- @param[optchain] ...
- _.unique = _.uniq
- --- Returns the duplicate-free union of all passed in arrays.
- -- @name union
- -- @tparam var_arg ... a variable number of arrays arguments
- -- @treturn table a new array
- function _.union(...)
- return _.uniq(_.flatten({...}))
- end
- --- Returns the intersection of all passed-in arrays.
- -- Each value in the result is present in each of the passed-in arrays.
- -- @name intersection
- -- @tparam table array an array
- -- @tparam var_arg ... a variable number of array arguments
- -- @treturn table a new array
- function _.intersection(array,...)
- local arg = {...}
- local _intersect = {}
- for i,value in ipairs(array) do
- if _.all(arg,function(i,v)
- return _.include(v,value)
- end) then
- t_insert(_intersect,value)
- end
- end
- return _intersect
- end
- --- Merges values of each of the passed-in arrays in subsets.
- -- Only values at the same position in the initial array are merged.
- -- @name zip
- -- @tparam var_arg ... a variable number of array arguments
- -- @treturn table a new array
- function _.zip(...)
- local arg = {...}
- local _len = _.max(_.map(arg,function(i,v)
- return #v
- end))
- local _ans = {}
- for i = 1,_len do
- _ans[i] = _.pluck(arg,i)
- end
- return _ans
- end
- --- Clones `array` and appends `other` to the result.
- -- @name append
- -- @tparam table array an array
- -- @tparam table other an array
- -- @treturn table a new array
- function _.append(array,other)
- local t = {}
- for i,v in ipairs(array) do t[i] = v end
- for i,v in ipairs(other) do t[#t+1] = v end
- return t
- end
- --- Produce a flexible list of numbers. If one positive value is passed, will count from 0 to that value,
- -- with a default step of 1. If two values were passed, will count from the first one to the second one, with the
- -- same default step of 1. A third passed value will be considered a step value.
- -- <br/><em>Aliased as @{count}</em>
- -- @name range
- -- @tparam var_arg ... a variable list of numbers
- -- @treturn table a new array of numbers
- -- @see count
- function _.range(...)
- local arg = {...}
- local _start,_stop,_step
- if #arg==0 then return {}
- elseif #arg==1 then _stop,_start,_step = arg[1],0,1
- elseif #arg==2 then _start,_stop,_step = arg[1],arg[2],1
- elseif #arg == 3 then _start,_stop,_step = arg[1],arg[2],arg[3]
- end
- if (_step and _step==0) then return {} end
- local _ranged = {}
- local _steps = max(floor((_stop-_start)/_step),0)
- for i=1,_steps do _ranged[#_ranged+1] = _start+_step*i end
- if #_ranged>0 then t_insert(_ranged,1,_start) end
- return _ranged
- end
- --- Alias for @{range}.
- -- @function count
- -- @param ...
- _.count = _.range
- --- Inverts `key-value` pairs. Keys becomes values, while values becomes keys.
- -- <br/><em>Aliased as @{mirror}</em>
- -- @name invert
- -- @tparam table array a given array
- -- @treturn table a new array
- -- @see mirror
- function _.invert(array)
- local _ret = {}
- _.each(array,function(i,v) _ret[v] = i end)
- return _ret
- end
- --- Alias for @{invert}.
- -- @function mirror
- -- @param array
- _.mirror = _.invert
- --- Concatenates values in a given array. Handles booleans as well. If `sep` string is
- -- passed, it will be used as a separator. Optional `i` and `j` will only concatenate
- -- values within `[i,j]` range. <br/><em>Aliased as @{join}</em>
- -- @name concat
- -- @tparam table array a given array
- -- @tparam[opt] string sep a separator string, defaults to `''`.
- -- @tparam[optchain] number i the starting index, defaults to 1.
- -- @tparam[optchain] number j the final index, defaults to the array length.
- -- @treturn string a string
- -- @see join
- function _.concat(array,sep,i,j)
- local _array = _.map(array,function(i,v)
- return tostring(v)
- end)
- return t_concat(_array,sep,i,j)
- end
- --- Alias for @{concat}.
- -- @function join
- -- @param array
- -- @param[opt] sep
- -- @param[optchain] i
- -- @param[optchain] j
- _.join = _.concat
- -- ========= Utility functions
- --- Returns the passed-in value. Used internally as a default iterator.
- -- @name identity
- -- @tparam arg value a value
- -- @treturn arg the passed-in value
- function _.identity(...)
- return ...
- end
- --- Returns a version of `f` that runs only once. Successive calls will make `f`
- -- keep yielding the same answer, no matter what the passed-in arguments are. Can be used to
- -- init variables as well.
- -- @name once
- -- @tparam function f a function
- -- @treturn function a new function
- function _.once(f)
- local _internal = 0
- local _args = {}
- return function(...)
- _internal = _internal+1
- if _internal<=1 then _args = {...} end
- return f(unpack(_args))
- end
- end
- --- Memoizes a given function by caching the computed result.
- -- Useful for speeding up slow-running functions. If function `hash` is passed,
- -- it will be used to compute unique keys from args passed to `hash` for results caching.
- -- <br/><em>Aliased as @{cache}</em>
- -- @name memoize
- -- @tparam function f a function
- -- @tparam[opt] function hash a hash function, defaults to @{identity}
- -- @treturn function a new function
- -- @see cache
- function _.memoize(f,hash)
- local _cache = setmetatable({},{__mode = 'kv'})
- local _hasher = hash or _.identity
- return function (...)
- local _hashKey = _hasher(...)
- local _result = _cache[_hashKey]
- if not _result then _cache[_hashKey] = f(...) end
- return _cache[_hashKey]
- end
- end
- --- Alias for @{memoize}.
- -- @function cache
- -- @param f
- -- @param[opt] hash
- _.cache = _.memoize
- --- Returns a version of `f` that runs on the `count-th` call.
- -- Useful when dealing with asynchronous tasks.
- -- @name after
- -- @tparam function f a function
- -- @tparam number count the number of calls before `f` answers
- -- @treturn function a new function
- function _.after(f,count)
- local _limit,_internal = count, 0
- return function(...)
- _internal = _internal+1
- if _internal >= _limit then return f(...) end
- end
- end
- --- Composes functions. Each passed-in function consumes the return value of the function that follows.
- -- In math terms, composing the functions `f`, `g`, and `h` produces `f(g(h(...)))`.
- -- @name compose
- -- @tparam var_arg ... a variable number of functions
- -- @treturn function a new function
- function _.compose(...)
- local f = _.reverse {...}
- return function (...)
- local _temp
- for i,func in pairs(f) do
- _temp = _temp and func(_temp) or func(...)
- end
- return _temp
- end
- end
- --- Wraps `f` inside of the `wrapper` function. It passes `f` as the first argument to `wrapper`.
- -- This allows the wrapper to execute code before and after `f` runs,
- -- adjust the arguments, and execute it conditionally.
- -- @name wrap
- -- @tparam function f a function to be wrapped, prototyped as `f(...)`
- -- @tparam function wrapper a wrapper function, prototyped as `wrapper(f,...)`
- -- @treturn function a new function
- function _.wrap(f,wrapper)
- return function (...)
- return wrapper(f,...)
- end
- end
- --- Runs `iter` function `n` times.
- -- Collects the results of each run and returns them in an array.
- -- @name times
- -- @tparam number n the number of times `iter` should be called
- -- @tparam function iter an iterator function, prototyped as `iter(i,...)`
- -- @tparam var_arg ... extra-args to be passed to `iter` function
- -- @treturn table an array of results
- function _.times(n,iter,...)
- local results = {}
- for i = 1,n do
- results[i] = iter(i,...)
- end
- return results
- end
- --- Binds `v` to be the first argument to function `f`. As a result,
- -- calling `f(...)` will result to `f(v,...)`.
- -- @name bind
- -- @tparam function f a function
- -- @tparam arg v an argument
- -- @treturn function a function, prototyped as `f(v,...)`
- function _.bind(f,v)
- return function (...)
- return f(v,...)
- end
- end
- --- Binds `...` to be the N-first arguments to function `f`. As a result,
- -- calling `f(a1,a2,...,aN)` will result to `f(...,a1,a2,...,aN)`.
- -- @name bindn
- -- @tparam function f a function
- -- @tparam var_arg ... a variable numer of arguments
- -- @treturn function a function, prototyped as `f(v,...)`
- function _.bindn(f,...)
- local iArg = {...}
- return function (...)
- return f(unpack(_.append(iArg,{...})))
- end
- end
- --- Generates a unique Id (unique for the current session). If given a sring `template`
- -- will use this template for output formatting. Otherwise, if `template` is a function,
- -- will compute an output running `template(id,...)`.
- -- <br/><em>Aliased as @{uId}</em>.
- -- @name uniqueId
- -- @tparam[opt] string|function template either a string or a function
- -- @tparam[optchain] var_arg ... a variable numer of arguments to be passed to `template`, in case it is a function.
- -- @treturn id a formatted Id
- -- @see uId
- function _.uniqueId(template,...)
- unique_id_counter = unique_id_counter + 1
- if template then
- if _.isString(template) then
- return template:format(unique_id_counter)
- elseif _.isFunction(template) then
- return template(unique_id_counter,...)
- end
- end
- return unique_id_counter
- end
- --- Alias for @{uniqueId}.
- -- @function uId
- -- @param[opt] template
- -- @param[optchain] ...
- _.uId = _.uniqueId
- -- ========= Object functions
- --- Returns the names of the object properties.
- -- @name keys
- -- @tparam table obj an object
- -- @treturn table an array
- function _.keys(obj)
- local _oKeys = {}
- _.each(obj,function(key,_) _oKeys[#_oKeys+1]=key end)
- return _oKeys
- end
- --- Returns the values of the object properties.
- -- @name values
- -- @tparam table obj an object
- -- @treturn table an array
- function _.values(obj)
- local _oValues = {}
- _.each(obj,function(_,value) _oValues[#_oValues+1]=value end)
- return _oValues
- end
- --- Returns an array of `obj` key-values pairs
- -- @name pairs
- -- @tparam table obj an object
- -- @treturn table an array
- function _.pairs(obj)
- local paired= {}
- _.each(obj,function(k,v)
- paired[#paired+1] = {k,v}
- end)
- return paired
- end
- --- Extends an object properties. It copies all of the properties of extra passed-in objects
- -- over to the destination object, and returns the destination object.
- -- The last object in `...` will override properties of the same name in the previous one
- -- @name extend
- -- @tparam table destObj a destination object
- -- @tparam var_arg ... a variable number of array arguments
- -- @treturn table the destination object extended
- function _.extend(destObj,...)
- local sources = {...}
- _.each(sources,function(__,source)
- if _.isObject(source) then
- _.each(source,function(key,value)
- destObj[key] = value
- end)
- end
- end)
- return destObj
- end
- --- Returns a sorted list of all methods names found in an object. If the given object
- -- has a metatable implementing an `__index` field pointing to another table, will also recurse on this
- -- table. If `output` is provided, will export results in this table.
- -- <br/><em>Aliased as @{methods}</em>.
- -- @name functions
- -- @tparam table obj an object
- -- @tparam[opt] table output Optional table to collect the results
- -- @treturn table an array of methods names
- -- @see methods
- function _.functions(obj,output)
- if not obj then return _.sort(_.keys(_)) end
- local _methods = output or {}
- _.each(obj,function(key,value)
- if _.isFunction(value) then
- _methods[#_methods+1]=key
- end
- end)
- local mt = getmetatable(obj)
- if mt and mt.__index then
- _.functions(mt.__index,_methods)
- end
- return _.sort(_methods)
- end
- --- Alias for @{functions}.
- -- @function methods
- -- @param obj
- -- @param[opt] output
- _.methods = _.functions
- --- Clones a given object properties. If `shallow` is passed
- -- will also clone nested array properties.
- -- @name clone
- -- @tparam table obj an object
- -- @tparam[opt] boolean shallow whether or not nested array-properties should be cloned, defaults to false.
- -- @treturn table a clone of the passed-in object
- function _.clone(obj,shallow)
- if not _.isObject(obj) then return obj end
- local _obj = {}
- _.each(obj,function(i,v)
- if _.isObject(v) then
- if not shallow then
- _obj[i] = _.clone(v,shallow)
- else _obj[i] = v
- end
- else
- _obj[i] = v
- end
- end)
- return _obj
- end
- --- Checks if a given object implements a property.
- -- @name has
- -- @tparam table obj an object
- -- @tparam key key a key property to be checked
- -- @treturn boolean __true__ or __false__
- function _.has(obj, key)
- return obj[key]~=nil
- end
- --- Return a filtered copy of the object. The returned object will only have
- -- the whitelisted properties. <br/><em>Aliased as @{choose}</em>.
- -- @name pick
- -- @tparam table obj an object
- -- @tparam var_arg ... a variable number of string keys
- -- @treturn table the filtered object
- -- @see choose
- function _.pick(obj, ...)
- local whitelist = _.flatten {...}
- local _picked = {}
- _.each(whitelist,function(key,property)
- if obj[property] then
- _picked[property] = obj[property]
- end
- end)
- return _picked
- end
- --- Alias for @{pick}.
- -- @function choose
- -- @param obj
- -- @param ...
- _.choose = _.pick
- --- Return a filtered copy of the object. The returned object will not have
- -- the blacklisted properties. <br/><em>Aliased as @{drop}</em>.
- -- @name omit
- -- @tparam table obj an object
- -- @tparam var_arg ... a variable number of string keys
- -- @treturn table the filtered object
- -- @see drop
- function _.omit(obj,...)
- local blacklist = _.flatten {...}
- local _picked = {}
- _.each(obj,function(key,value)
- if not _.include(blacklist,key) then
- _picked[key] = value
- end
- end)
- return _picked
- end
- --- Alias for @{omit}.
- -- @function drop
- -- @param obj
- -- @param ...
- _.drop = _.omit
- --- Fills nil properties in an object with the given `template` object. Pre-existing
- -- properties will be preserved. <br/><em>Aliased as @{defaults}</em>.
- -- @name template
- -- @tparam table obj an object
- -- @tparam table template a template object
- -- @treturn table the passed-in object filled
- -- @see defaults
- function _.template(obj,template)
- _.each(template,function(i,v)
- if not obj[i] then
- obj[i] = v
- end
- end)
- return obj
- end
- --- Alias for @{template}.
- -- @function defaults
- -- @param obj
- -- @param template
- _.defaults = _.template
- --- Performs an equality test between two objects.
- -- Can compare strings, functions (by reference), nil, booleans. Compares tables by reference or by component-values.
- -- Comparing objects, if `useMt` is passed, the equality operator `==` will be used if one of
- -- the given objects has a metatable implementing `mt.__eq`
- -- @name isEqual
- -- @tparam table objA an object
- -- @tparam table objB another object
- -- @tparam[opt] boolean useMt whether or not `mt.__eq` should be used, defaults to false.
- -- @treturn boolean __true__ or __false__
- function _.isEqual(objA,objB,useMt)
- local typeObjA = type(objA)
- local typeObjB = type(objB)
- if typeObjA~=typeObjB then return false end
- if typeObjA~='table' then return (objA==objB) end
- local mtA = getmetatable(objA)
- local mtB = getmetatable(objB)
- if useMt then
- if mtA or mtB and mtA.__eq or mtB.__eq then
- return (objA==objB)
- end
- end
- if _.size(objA)~=_.size(objB) then return false end
- for i,v1 in pairs(objA) do
- local v2 = objB[i]
- if _.isNil(v2) or not _.isEqual(v1,v2,useMt) then return false end
- end
- for i,v1 in pairs(objB) do
- local v2 = objA[i]
- if _.isNil(v2) then return false end
- end
- return true
- end
- --- Invokes an object method. It passes the object itself as the first argument. if `method` is not
- -- callable, will return `obj[method]`.
- -- @name result
- -- @tparam table obj an object
- -- @tparam string method a string key to index in object `obj`.
- -- @tparam[opt] var_arg ... Optional extra-args to be passed to `method`
- -- @treturn value the returned value of `method(obj,...)` call
- function _.result(obj,method,...)
- if obj[method] then
- if _.isCallable(obj[method]) then
- return obj[method](obj,...)
- else return obj[method]
- end
- end
- if _.isCallable(method) then
- return method(obj,...)
- end
- end
- --- Checks if the given arg is an object (i.e a Lua table).
- -- @name isObject
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isObject(obj)
- return type(obj) == 'table'
- end
- --- Checks if the given arg is an callable. Assumes `obj` is callable if
- -- it is either a function or a table having a metatable implementing `__call` metamethod.
- -- @name isCallable
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isCallable(obj)
- return (_.isFunction(obj) or
- (_.isObject(obj) and getmetatable(obj)
- and getmetatable(obj).__call~=nil) or false)
- end
- --- Checks if the given arg is an array. Assumes `obj` is an array
- -- if is a table with integer numbers as indexes.
- -- @name isArray
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isArray(obj)
- if not _.isObject(obj) then return false end
- return _.all(_.keys(obj),function(i,v)
- return _.isNumber(v) and (floor(v)==v)
- end)
- end
- --- Checks if the given is empty. If `obj` is a @{string}, will return __true__
- -- if `#obj==0`. Otherwise, if `obj` is a table, will return whether or not this table
- -- is empty.
- -- @name isEmpty
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isEmpty(obj)
- if _.isString(obj) then return #obj==0 end
- if _.isObject(obj) then return next(obj)==nil end
- return true
- end
- --- Checks if the given arg is a @{string}.
- -- @name isString
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isString(obj)
- return type(obj) == 'string'
- end
- --- Checks if the given arg is a function.
- -- @name isFunction
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isFunction(obj)
- return type(obj) == 'function'
- end
- --- Checks if the given arg is nil.
- -- @name isNil
- -- @tparam object obj an object
- -- @treturn boolean __true__ or __false__
- function _.isNil(obj)
- return obj==nil
- end
- --- Checks if the given arg is a finite number.
- -- @name isFinite
- -- @tparam object obj a number
- -- @treturn boolean __true__ or __false__
- function _.isFinite(obj)
- if not _.isNumber(obj) then return false end
- return obj > -huge and obj < huge
- end
- --- Checks if the given arg is a number.
- -- @name isNumber
- -- @tparam object obj a number
- -- @treturn boolean __true__ or __false__
- function _.isNumber(obj)
- return type(obj) == 'number'
- end
- --- Checks if the given arg is NaN (see [Not-A-Number](http://en.wikipedia.org/wiki/NaN)).
- -- @name isNaN
- -- @tparam object obj a number
- -- @treturn boolean __true__ or __false__
- function _.isNaN(obj)
- return _.isNumber(obj) and obj~=obj
- end
- --- Checks if the given arg is a boolean.
- -- @name isBoolean
- -- @tparam object obj a boolean
- -- @treturn boolean __true__ or __false__
- function _.isBoolean(obj)
- return type(obj) == 'boolean'
- end
- --- Imports all library functions in the global environment. <br/><em>Aliased as @{mixin}</em>
- -- @name import
- -- @see mixin
- local function import()
- local fn = _.functions()
- local env = getfenv()
- _.each(fn,function(i,fName)
- env[fName] = _[fName]
- end)
- end
- --- Alias for @{import}.
- -- @function mixin
- local mixin = import
- local _mt = {import = import, mixin = mixin}
- _mt.__index = _mt
- return setmetatable (_, _mt)]===================],"CoolisOS/bin/kernel/checker",true,"CoolisOS/bin/kernel/checker/init.lua",[===================[local classname=require'kernel.class'.classname
- custom={}
- function conforms(t,a)
- local k=t:sub(1, 1)
- return t == "?"
- or (k == "?" and (a==nil or conforms(t:sub(2, -1),a)))
- or (k == "!" and not (conforms(t:sub(2, -1),a)))
- or type(a)==t
- or classname(a) == t
- or (custom[t] and custom[t](a))
- end
- function check_one(d,a)
- for t in d:gmatch('|?([^|]+)|?') do
- if conforms(t,a) then return true end
- end
- return false
- end
- function check(s,...)
- if type(s)~='string' then error('arg1 is not of type string',2) end
- local i=0
- for typ in s:gmatch(',?([^,]+),?') do
- i=i+1
- if not check_one(typ,select(i,...)) then
- local msg=string.format(
- 'arg%d:(%s) is not of type (%s);\n args=%s',
- i,tostring((debug and debug.getinfo or type)((select(i,...)))),typ,
- (debug.args_tostring(...)))
- error(msg,3)
- end
- end
- end]===================],"CoolisOS/bin/kernel/class",true,"CoolisOS/bin/kernel/class/Box.lua",[===================[include'kernel.class'
- local Opt=require'kernel.class.Opt'
- local compat=require'kernel.compat'
- local check=require'kernel.checker'.check
- local pipe=sched.pipe
- class.Box()
- Box.token={}
- do
- local def=Opt{
- pin=Opt(pipe),
- pout=Opt(pipe),
- perr=Opt(pipe),
- cc=false,
- terminal=Opt(nil),
- env=Opt(function()
- local env=util.shcopy(_G)
- env._G=env
- return env
- end)
- }
- function Box:__init(opt)
- def:drop(self,opt)
- self.env[Box.token]=self
- self.env._BOX=self
- if self.terminal then
- self.env.write=function(...)
- return self.terminal:write(...)
- end
- self.env.read=function(...)
- return self.terminal:read(...)
- end
- self.env.printError=function(s)
- return debug.printError(s,self.terminal)
- end
- self.env.term=self.terminal.term
- else
- self.env.print=function(s)
- self.pout:send(s)
- end
- self.env.write=function(s)
- self.pout:send(s)
- end
- self.env.read=function()
- self.pin:receive()
- end
- self.env.printError=function(s)
- self.perr:send(s)
- end
- end
- self.env.os=util.shcopy(self.env.os)
- self.env.os.execute=function(s)
- if not self.env.shell then error'no shell' end
- return self.env.shell.execute(s)
- end
- end
- function Box:run(f,...)
- setfenv(f,self.env)
- return f(...)
- end
- Box.get_current=function(level)
- level=(level or 0)
- repeat
- level=level+1
- local env=getfenv(level)
- if env[Box.token] then
- return env[Box.token]
- end
- until env==_Gstore.base_env
- end
- end
- return Box]===================],"CoolisOS/bin/kernel/class/init.lua",[===================[-- classlib.lua 2.04.04
- --
- -- Changes from 2.03:
- --
- -- 1. Included patches and ideas from Peter Schaefer
- -- ([email protected]) considerably improving the efficiency of
- -- build(), __init() and other parts of the code.
- --
- -- 2. The former remove_ambiguous() function was removed, improving the
- -- efficiency of both build() (instance creation) and mt:__call() (class
- -- creation). Ambiguities are now processed using the ambiguous_keys
- -- tables introduced by Peter.
- --
- -- 3. Removed inheritance of class properties, which was inconsistent with
- -- the way instance properties are handled (see pages 4-5 of the manual).
- -- Mostly harmless, but confusing. Now only methods are inherited.
- --
- -- PRIVATE
- --[[
- Define unique value for identifying ambiguous base objects and inherited
- attributes. Ambiguous values are normally removed from classes and objects,
- but if keep_ambiguous == true they are left there and the ambiguous value
- is made to behave in a way useful for debugging.
- ]]
- --[[
- Changes for OS:
- -__root field of each self points to the deepest self table;
- -methods matching '^_[^_]' are not copied into subclasses;
- ]]
- local ambiguous
- if keep_ambiguous then
- ambiguous = { _type = 'ambiguous' }
- local function invalid(operation)
- return function()
- error('Invalid ' .. operation .. ' on ambiguous')
- end
- end
- -- Make ambiguous complain about everything except tostring()
- local ambiguous_mt =
- {
- __add = invalid('addition'),
- __sub = invalid('substraction'),
- __mul = invalid('multiplication'),
- __div = invalid('division'),
- __mod = invalid('modulus operation'),
- __pow = invalid('exponentiation'),
- __unm = invalid('unary minus'),
- __concat = invalid('concatenation'),
- __len = invalid('length operation'),
- __eq = invalid('equality comparison'),
- __lt = invalid('less than'),
- __le = invalid('less or equal'),
- __index = invalid('indexing'),
- __newindex = invalid('new indexing'),
- __call = invalid('call'),
- __tostring = function() return 'ambiguous' end,
- __tonumber = invalid('conversion to number')
- }
- setmetatable(ambiguous, ambiguous_mt)
- end
- --[[
- Reserved attribute names.
- ]]
- local reserved =
- {
- __index = true,
- __newindex = true,
- __type = true,
- __class = true,
- __bases = true,
- __inherited = true,
- __from = true,
- __shared = true,
- __user_init = true,
- __name = true,
- __initialized = true,
- __root = true,
- }
- --[[
- Some special user-set attributes are renamed.
- ]]
- local rename =
- {
- __init = '__user_init',
- __set = '__user_set',
- __get = '__user_get'
- }
- --[[
- The metatable of all classes, containing:
- To be used by the classes:
- __call() for creating instances
- __init() default constructor
- is_a() for checking object and class types
- implements() for checking interface support
- For internal use:
- __newindex() for controlling class population
- ]]
- local class_mt = {}
- class_mt.__index = class_mt
- --[[
- This controls class population.
- Here 'self' is a class being populated by inheritance or by the user.
- ]]
- function class_mt:__newindex(name, value)
- -- Rename special user-set attributes
- if rename[name] then name = rename[name] end
- -- __user_get() needs an __index() handler
- if name == '__user_get' then
- self.__index = value and function(obj, k)
- local v = self[k]
- if v == nil and not reserved[k] then v = value(obj, k) end
- return v
- end or self
- -- __user_set() needs a __newindex() handler
- elseif name == '__user_set' then
- self.__newindex = value and function(obj, k, v)
- if reserved[k] or not value(obj, k, v) then rawset(obj, k, v) end
- end or nil
- end
- -- Assign the attribute
- rawset(self, name, value)
- end
- --[[
- This function creates an object of a certain class and calls itself
- recursively to create one child object for each base class. Base objects
- of unnamed base classes are accessed by using the base class as an index
- into the object, base objects of named base classes are accessed as fields
- of the object with the names of their respective base classes.
- Classes derived in shared mode will create only a single base object.
- Unambiguous grandchildren are inherited by the parent if they do not
- collide with direct children.
- ]]
- local function build(class, shared_objs, shared,__root)
- -- If shared, look in the repository of shared objects
- -- and return any previous instance of this class.
- if shared then
- local prev_instance = shared_objs[class]
- if prev_instance then return prev_instance end
- end
- -- Create new object
- local obj = { __type = 'object' }
- if __root==nil then
- __root=obj
- end
- obj.__root=__root
- obj[class.__name]=obj
- -- Build child objects if there are base classes
- local nbases = #class.__bases
- if nbases > 0 then
- -- Repository for storing inherited base objects
- local inherited = {}
- -- List of ambiguous keys
- local ambiguous_keys = {}
- -- Build child objects for each base class
- for i = 1, nbases do
- local base = class.__bases[i]
- local child = build(base, shared_objs, class.__shared[base],__root)
- obj[base.__name] = child
- -- Get inherited grandchildren from this child
- for c, grandchild in pairs(child) do
- -- We can only accept one inherited grandchild of each class,
- -- otherwise this is an ambiguous reference
- if not ambiguous_keys[c] then
- if not inherited[c] then inherited[c] = grandchild
- elseif inherited[c] ~= grandchild then
- inherited[c] = ambiguous
- table.insert(ambiguous_keys, c)
- end
- end
- end
- end
- -- Accept inherited grandchildren if they don't collide with
- -- direct children
- for k, v in pairs(inherited) do
- if not obj[k] then obj[k] = v end
- end
- end
- -- Object is ready
- setmetatable(obj, class)
- -- If shared, add it to the repository of shared objects
- if shared then shared_objs[class] = obj end
- return obj
- end
- --[[
- The __call() operator creates an instance of the class and initializes it.
- ]]
- function class_mt:__call(...)
- local obj = build(self, {}, false)
- obj:__init(...)
- return obj
- end
- --[[
- The implements() method checks that an object or class supports the
- interface of a target class. This means it can be passed as an argument to
- any function that expects the target class. We consider only functions
- and callable objects to be part of the interface of a class.
- ]]
- function class_mt:implements(class)
- -- Auxiliary function to determine if something is callable
- local function is_callable(v)
- if v == ambiguous then return false end
- if type(v) == 'function' then return true end
- local mt = getmetatable(v)
- return mt and type(mt.__call) == 'function'
- end
- -- Check we have all the target's callables (except reserved names)
- for k, v in pairs(class) do
- if not reserved[k] and is_callable(v) and not is_callable(self[k]) then
- return false
- end
- end
- return true
- end
- --[[
- The is_a() method checks the type of an object or class starting from
- its class and following the derivation chain upwards looking for
- the target class. If the target class is found, it checks that its
- interface is supported (this may fail in multiple inheritance because
- of ambiguities).
- ]]
- function class_mt:is_a(class)
- -- If our class is the target class this is trivially true
- if self.__class == class then return true end
- -- Auxiliary function to determine if a target class is one of a list of
- -- classes or one of their bases
- local function find(target, classlist)
- for i = 1, #classlist do
- local class = classlist[i]
- if class == target or find(target, class.__bases) then
- return true
- end
- end
- return false
- end
- -- Check that we derive from the target
- if not find(class, self.__bases) then return false end
- -- Check that we implement the target's interface.
- return self:implements(class)
- end
- --[[
- Factory-supplied constructor, calls the user-supplied constructor if any,
- then calls the constructors of the bases to initialize those that were
- not initialized before. Objects are initialized exactly once.
- ]]
- function class_mt:__init(...)
- if self.__initialized then return end
- if self.__user_init then self:__user_init(...) end
- for i = 1, #self.__bases do
- local base = self.__bases[i]
- self[base.__name]:__init(...)
- end
- self.__initialized = true
- end
- -- PUBLIC
- --[[
- Utility type and interface checking functions
- ]]
- function ofType(value)
- local t = type(value)
- return t =='table' and classname(t) or t
- end
- function typeof(value)
- local t = type(value)
- return t =='table' and value.__type or t
- end
- function classof(value)
- local t = type(value)
- return t == 'table' and value.__class or nil
- end
- function classname(value)
- if not classof(value) then return nil end
- local name = value.__name
- return type(name) == 'string' and name or nil
- end
- function implements(value, class)
- return classof(value) and value:implements(class) or false
- end
- function is_a(value, class)
- return classof(value) and value:is_a(class) or false
- end
- --[[
- Use a table to control class creation and naming.
- ]]
- class = {}
- local mt = {}
- setmetatable(class, mt)
- --[[
- Create a named or unnamed class by calling class([name, ] ...).
- Arguments are an optional string to set the class name and the classes or
- shared classes to be derived from.
- ]]
- function mt:__call(...)
- local arg = {...}
- -- Create a new class
- local c =
- {
- __type = 'class',
- __bases = {},
- __shared = {}
- }
- c.__class = c
- c.__index = c
- -- A first string argument sets the name of the class.
- if type(arg[1]) == 'string' then
- c.__name = arg[1]
- table.remove(arg, 1)
- else
- c.__name = c
- end
- -- Repository of inherited attributes
- local inherited = {}
- local from = {}
- -- List of ambiguous keys
- local ambiguous_keys = {}
- -- Inherit from the base classes
- for i = 1, #arg do
- local base = arg[i]
- -- Get the base and whether it is inherited in shared mode
- local basetype = typeof(base)
- local shared = basetype == 'share'
- assert(basetype == 'class' or shared,
- 'Base ' .. i .. ' is not a class or shared class')
- if shared then base = base.__class end
- -- Just in case, check this base is not repeated
- assert(c.__shared[base] == nil, 'Base ' .. i .. ' is duplicated')
- -- Accept it
- c.__bases[i] = base
- c.__shared[base] = shared
- -- Get methods that could be inherited from this base
- for k, v in pairs(base) do
- -- Skip reserved and ambiguous methods
- if type(v) == 'function' and not reserved[k] and (type(k)=='string' and not k:match'^_[^_]')and
- not ambiguous_keys[k] then
- -- Where does this method come from?
- local new_from
- -- Check if the method was inherited by the base
- local base_inherited = base.__inherited[k]
- if base_inherited then
- -- If it has been redefined, cancel this inheritance
- if base_inherited ~= v then -- (1)
- base.__inherited[k] = nil
- base.__from[k] = nil
- -- It is still inherited, get it from the original
- else
- new_from = base.__from[k]
- end
- end
- -- If it is not inherited by the base, it originates there
- new_from = new_from or { class = base, shared = shared }
- -- Accept a first-time inheritance
- local current_from = from[k]
- if not current_from then
- from[k] = new_from
- local origin = new_from.class
- -- We assume this is an instance method (called with
- -- self as first argument) and wrap it so that it will
- -- receive the correct base object as self. For class
- -- functions this code is unusable.
- inherited[k] = function(self, ...)
- return origin[k](self[origin.__name], ...)
- end
- -- Methods inherited more than once are ambiguous unless
- -- they originate in the same shared class.
- elseif current_from.class ~= new_from.class or
- not current_from.shared or not new_from.shared then
- inherited[k] = ambiguous
- table.insert(ambiguous_keys, k)
- from[k] = nil
- end
- end
- end
- end
- -- Set the metatable now, it monitors attribute setting and does some
- -- special processing for some of them.
- setmetatable(c, class_mt)
- -- Set inherited attributes in the class, they may be redefined afterwards
- for k, v in pairs(inherited) do c[k] = v end -- checked at (1)
- c.__inherited = inherited
- c.__from = from
- return c
- end
- --[[
- Create a named class and assign it to a global variable of the same name.
- Example: class.A(...) is equivalent to (global) A = class('A', ...).
- ]]
- function mt:__index(name)
- return function(...)
- local c = class(name, ...)
- local cenv=getfenv(2)
- cenv[name] = c
- return c
- end
- end
- --[[
- Wrap a class for shared derivation.
- ]]
- function shared(class)
- assert(typeof(class) == 'class', 'Argument is not a class')
- return { __type = 'share', __class = class }
- end
- ]===================],"CoolisOS/bin/kernel/class/Opt.lua",[===================[class.Opt()
- function Opt:__init(t,typ,...)
- self.typ=typ or (typeof(t))
- self.val=t
- self.args={...}
- end
- function Opt:drop(dst,src,name)
- local typ=self.typ
- if typ=='req' then
- if src==nil then error(string.format('parameter %s required',tostring(name)))
- else return src end
- elseif typ=='function' then if src==nil then return self.val(unpack(self.args)) else return src end
- elseif typ=='raw' or typ=='nil' then
- if src==nil then return self.val else return src end
- elseif typ=='object' then return src==nil and (self.val.clone and self.val:clone() or self.val.__class(self.val)) or src
- elseif typ=='class' then return src==nil and (self.val(unpack(self.args))) or src
- elseif typ=='table' then
- dst=dst or {}
- src=src or {}
- for i,v in pairs(self.val) do
- if classof(v)==Opt then
- dst[i]=v:drop(nil,src[i],i)
- else
- if src[i]~=nil then
- v=src[i]
- end
- dst[i]=v
- end
- end
- return dst
- end
- end
- return Opt]===================],"CoolisOS/bin/kernel/class/Var.lua",[===================[include'kernel.class'
- local check=require'kernel.checker'
- local dump=require'utils.serpent'.dump
- local util=require'kernel.util'
- class.Var()
- local p=setmetatable({},{__mode='k'})
- function Var:__init(file,opt)
- p[self]={}
- p[self].file=file
- local dir=loadreq.getDir(p[self].file)
- if not fs.exists(dir) then fs.makeDir(dir) end
- if not fs.exists(file) then self:setAll{} end
- end
- function Var:getAll()
- local f=io.open(p[self].file,'r')
- local s=f:read'*a'
- f:close()
- local t=loadstring(s)()
- return t
- end
- function Var:setAll(t)
- local f=io.open(p[self].file,'w')
- local s=f:write(dump(t))
- f:close()
- end
- function Var:__get(k)
- return self:getAll()[k]
- end
- function Var:__set(k,v)
- local t=self:getAll()
- t[k]=v
- self:setAll(t)
- return true
- end
- return Var]===================],"CoolisOS/bin/kernel/gui",true,"CoolisOS/bin/kernel/gui/autocomplete.lua",[===================[
- local list=require'utils.pl.List'
- local pathutils={} --API
- do
- local segments, get, concat, find
- local function pathconcat(pt, starti, endi)
- local t = {}
- local prev
- local empties = 0
- starti = starti or 1
- endi = endi or #pt
- for i = starti, endi do
- local v = pt[i]
- if not v then break
- elseif v == '' then
- empties = empties+1
- else
- table.insert(t, prev)
- prev = v
- end
- end
- table.insert(t, prev)
- --log('PATH', 'INFO', "pathconcat(%s, %d, %d) generates table %s, wants indexes %d->%d",
- -- sprint(pt), starti, endi, sprint(t), 1, endi-starti+1-empties)
- return table.concat(t, '.', 1, endi-starti+1-empties)
- end
- function get(t, path)
- local p = type(path)=='string' and segments(path) or path
- local k = table.remove(p)
- if not k then return t end
- local t = find(t, p)
- return t and t[k]
- end
- function segments(path)
- local t = {}
- local index, newindex, elt = 1
- repeat
- newindex = path:find(".", index, true) or #path+1 --last round
- elt = path:sub(index, newindex-1)
- elt = tonumber(elt) or elt
- if elt and elt ~= "" then table.insert(t, elt) end
- index = newindex+1
- until newindex==#path+1
- return t
- end
- function find(t, path, force)
- path = type(path)=="string" and segments(path) or path
- for i, n in ipairs(path) do
- local v = t[n]
- if type(v) ~= "table" then
- if not force or (force=="noowr" and v~=nil) then return nil, pathconcat(path, 1, i)
- else v = {} t[n] = v end
- end
- t = v
- end
- return t
- end
- pathutils.get=get
- end
- function env_matcher(path,opts)
- local env=opts.env or _G
- local lpath,pa = path:match("^(.-)([%w_]?[%w_%.%:]*)$") -- get the significant end part of the path (non alphanum are spliters...)
- local p, s, l = pa:match("(.-)([%.%:]?)([^%.%:]*)$") -- separate into sub path and leaf, getting the separator
- local t = pathutils.get(env, p)
- local funconly = s == ":"
- local tr = {}
- local function copykeys(src, dst)
- if type(src) ~= 'table' then return end
- for k, v in pairs(src) do
- local tv = type(v)
- if type(k)=='string' and k:match("^"..l) and (not funconly or tv=='function' or (tv=='table' and getmetatable(v) and getmetatable(v).__call)) then dst[k] = true end
- end
- end
- copykeys(t, tr)
- if s == ":" or s == "." then
- local m = getmetatable(t)
- if m then
- local i, n = m.__index, m.__newindex
- copykeys(i, tr)
- if n ~= i then copykeys(n, tr) end
- --if m~= i and m ~= n then copykeys(m, tr) end -- far from being perfect, but it happens that __index or __newindex are functions that uses the metatable as a object method holder
- end
- end
- local r = {}
- for k, v in pairs(tr) do table.insert(r, k) end
- -- sort the entries by lexical order
- table.sort(r)
- --when we have a complete match we can add a ".", ":" or "(" if the object is a table or a function
- if l == r[1] then
- local v=t[l]
- if type(v)=='function' then
- r[1]=r[1].."("
- elseif type(v)=='table' then
- if typeof(v)=='object' then
- r[1]=r[1]..":"
- else
- r[1]=r[1].."."
- end
- end
- end
- for i,v in pairs(r) do r[i]=lpath..p..s..v end
- return r
- end
- local tKeywords = {
- ["and"] = true,
- ["break"] = true,
- ["do"] = true,
- ["else"] = true,
- ["elseif"] = true,
- ["end"] = true,
- ["false"] = true,
- ["for"] = true,
- ["function"] = true,
- ["if"] = true,
- ["in"] = true,
- ["local"] = true,
- ["nil"] = true,
- ["not"] = true,
- ["or"] = true,
- ["repeat"] = true,
- ["return"] = true,
- ["then"] = true,
- ["true"] = true,
- ["until"]= true,
- ["while"] = true,
- }
- function keyword_matcher(path,opts)
- local r={}
- if #path~=0 then
- for key in pairs(tKeywords) do
- if path==key:sub(1,#path) then
- table.insert(r,key)
- end
- end
- end
- return r
- end
- local function esc(x)
- return (x:gsub('%%', '%%%%')
- :gsub('%^', '%%%^')
- :gsub('%$', '%%%$')
- :gsub('%(', '%%%(')
- :gsub('%)', '%%%)')
- :gsub('%.', '%%%.')
- :gsub('%[', '%%%[')
- :gsub('%]', '%%%]')
- :gsub('%*', '%%%*')
- :gsub('%+', '%%%+')
- :gsub('%-', '%%%-')
- :gsub('%?', '%%%?'))
- end
- function fs_matcher(word,opts)
- local matches={}
- local p,s,l
- l=word:match("[^/\\]*$")
- local cwd=opts.cwd or '/'
- local absDir
- if not word:match("[/\\][^/\\]*$") then
- absDir=cwd
- p,s='',''
- else
- p,s=word:match("^(.-)([/\\])[^/\\]*$")
- absDir=(p=='' and '' or fs.combine(cwd,p))
- end
- if fs.isDir( absDir ) then
- -- Search for matches in the resolved folder.
- local ok, tFileList = pcall( fs.list, absDir )
- if ok then
- -- Populate table with all matches.
- local pat="^"..esc(l)..'.*'
- for k, v in ipairs(tFileList) do
- local match = string.match( v, pat)
- if match then
- -- print(match)
- table.insert( matches,match )
- end
- end
- end
- end
- table.sort(matches)
- if (l == matches[1]) and fs.isDir(fs.combine(absDir,l)) then
- matches[1]= matches[1]..'/'
- end
- for i,v in pairs(matches) do matches[i]=p..s..v end
- return matches
- end
- function getmatch(path,opts)
- local r=list()
- local matchers=opts.matchers
- for _,matcher in pairs(matchers) do
- r:extend(matcher(path,opts))
- end
- r:sort()
- return r
- end
- ]===================],"CoolisOS/bin/kernel/gui/EvtH.lua",[===================[class.EvtH()
- do
- function EvtH:__init()
- end
- function EvtH:destroy()
- end
- function EvtH:queue()
- end
- wxEvtHandler::Connect
- function EvtH:connect(src,event,f,...)
- local cast=src.EvtH
- local t=cast.dst[event] or setmetatable({},{__mode='k'})
- cast.dst[event][self]=true
- t=self.handlers[event] or {}
- self.handlers[event]=t
- t[f]={...}
- end
- wxEvtHandler::Disconnect
- function EvtH:disconnect(src,event,f,this)
- srcs=src and {src=true} or self.src
- for src in pairs(srcs) do
- events=event and {event=src[event]} or src.dst
- for event,evth in pairs(events) do
- fs=f and {f}
- end
- end
- end
- wxEvtHandler::GetClientData
- function EvtH:()
- end
- wxEvtHandler::GetClientObject
- function EvtH:()
- end
- wxEvtHandler::GetEvtHandlerEnabled
- function EvtH:isEnabled()
- end
- wxEvtHandler::GetNextHandler
- function EvtH:next()
- end
- wxEvtHandler::GetPreviousHandler
- function EvtH:previous()
- end
- wxEvtHandler::ProcessEvent
- function EvtH:process()
- end
- wxEvtHandler::SearchEventTable
- function EvtH:search()
- end
- wxEvtHandler::SetClientData
- function EvtH:()
- end
- wxEvtHandler::SetClientObject
- function EvtH:()
- end
- function EvtH:setEnabled()
- end
- function EvtH:setNext()
- end
- function EvtH:setPrevious()
- end
- end]===================],"CoolisOS/bin/kernel/gui/init.lua",[===================[include'kernel.class'
- local sched=require'kernel.sched'
- local checker=require'kernel.checker'
- local check=checker.check
- local list=require'utils.pl.List'
- gui=getfenv()
- Point=require'Point'
- Rectangle=require'Rectangle'
- Term=require'Term'
- function read(pin,term,opts)
- pin=pin.Pipe
- term:setCursorBlink(true)
- local _sReplaceChar=opts._sReplaceChar
- local _tHistory=opts.history
- local sLine = ""
- local nHistoryPos = nil
- local nPos = 0
- if _sReplaceChar then
- _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
- end
- local auto
- if opts.auto then
- assert(#opts.auto>0)
- auto={}
- auto.mode=opts.auto.mode or 1
- auto.count=0
- auto.matches = { n = 0}
- function auto.reset()
- auto.matches = { n = 0}
- end
- local function split(s,n)
- local s=sLine
- local n=nPos+1
- local l,r=s:sub(1,n-1),s:sub(n)
- local pl,rl
- if l:sub(-1,-1):match'%s' then
- local sp=l:sub(-1,-1)
- l=l:sub(1,-2)
- r=sp..r
- end
- local ls,lp=l:match'^(.-)([^%s]+)$'
- rp,rs=r:match'^([^%s]+)(.-)$'
- if lp==nil then ls,lp=l,'','' end
- if rp==nil then rp,rs='',r end
- return ls,lp..rp,rs
- end
- function auto.update()
- local l,p,r=split(sLine,nPos+1)
- -- debug.log(l,p,r)
- local ok,m=pcall(opts.auto[auto.mode],p)
- if not ok then debug.log(m) end
- -- debug.log(pstring(m,1))
- m.l,m.r=l,r
- auto.matches=m
- end
- function auto.put()
- local matches=auto.matches
- local match=matches[matches.n]
- local l,r=matches.l,matches.r
- sLine = l..match..r
- nPos = #l+#match
- end
- end
- local w, h = term:getSize()
- local sx, sy = term:getCursorPos()
- local function redraw( _sCustomReplaceChar )
- local nScroll = 0
- local su=''
- if sx + nPos > w then
- nScroll = (sx + nPos) - w
- end
- term:setCursorPos( sx, sy )
- local sReplace = _sCustomReplaceChar or _sReplaceChar
- local s
- if sReplace then
- s=string.rep(sReplace, string.len(sLine) - nScroll)
- else
- s=string.sub( sLine, nScroll + 1 )
- end
- if #s<(w-sx+1) then s=s..(' '):rep(w-sx+1) end
- term:write(s)
- term:setCursorPos( sx + nPos - nScroll, sy )
- end
- pin:reset()
- while true do
- local _,sEvent,param=unpack(pin:receive())
- if sEvent == "char" then
- if auto then auto.reset() end
- sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
- nPos = nPos + 1
- redraw()
- elseif sEvent == "key" then
- if auto then
- if not (param == keys.tab) then
- auto.reset() -- Reset completion match-table.
- else
- if auto.matches[1] then -- tab was pressed before.
- local matches=auto.matches
- -- Shift pointer to next match.
- matches.n = (matches.n)%#matches +1 -- Wrap around if the pointer has gone past the end.
- -- Assemble the new line.
- auto.put()
- redraw()
- else
- -- [[ No matches yet, look for some now. ]]
- auto.update()
- local matches=auto.matches
- matches.n=0
- if #matches==0 then
- --nPos=nPos+4
- --sLine=sLine..'\t'
- else -- Show first match.
- matches.n=1
- auto.put()
- if #matches==1 then auto.reset() end
- end
- redraw()
- end
- end
- if not (param == keys.rightShift) then
- auto.count=0
- else
- auto.count=(auto.count+1)%2
- if auto.count==0 then
- auto.mode=(auto.mode)%#opts.auto+1
- end
- auto.reset()
- end
- end
- if param == keys.enter then
- -- Enter
- break
- elseif param == keys.left then
- -- Left
- if nPos > 0 then
- nPos = nPos - 1
- redraw()
- end
- elseif param == keys.right then
- -- Right
- if nPos < string.len(sLine) then
- nPos = nPos + 1
- redraw()
- end
- elseif param == keys.up or param == keys.down then
- -- Up or down
- if _tHistory then
- redraw(" ");
- if param == keys.up then
- -- Up
- if nHistoryPos == nil then
- if #_tHistory > 0 then
- nHistoryPos = #_tHistory
- end
- elseif nHistoryPos > 1 then
- nHistoryPos = nHistoryPos - 1
- end
- else
- -- Down
- if nHistoryPos == #_tHistory then
- nHistoryPos = nil
- elseif nHistoryPos ~= nil then
- nHistoryPos = nHistoryPos + 1
- end
- end
- if nHistoryPos then
- sLine = _tHistory[nHistoryPos]
- nPos = string.len( sLine )
- else
- sLine = ""
- nPos = 0
- end
- redraw()
- end
- elseif param == keys.backspace then
- -- Backspace
- if nPos > 0 then
- redraw(" ");
- sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
- nPos = nPos - 1
- redraw()
- end
- elseif param == keys.home then
- -- Home
- nPos = 0
- redraw()
- elseif param == keys.delete then
- if nPos < string.len(sLine) then
- redraw(" ");
- sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
- redraw()
- end
- elseif param == keys["end"] then
- -- End
- nPos = string.len(sLine)
- redraw()
- -- elseif param == keys.leftAlt then
- -- write('\n'..'..')
- -- if auto then opts.auto.mode=auto.mode end
- -- return sLine..'\n'..read(pin,term,opts)
- end
- end
- end
- term:setCursorBlink( false )
- term:setCursorPos( w + 1, sy )
- if opts.clear~=false then print() end
- return sLine
- end
- function write(term,sText)
- check('string',sText)
- local w,h = term:getSize()
- local x,y = term:getCursorPos()
- local nLinesPrinted = 0
- local function newLine()
- if y + 1 <= h then
- term:setCursorPos(1, y + 1)
- else
- term:setCursorPos(1, h)
- term:scroll(1)
- end
- x, y = term:getCursorPos()
- nLinesPrinted = nLinesPrinted + 1
- end
- -- Print the line with proper word wrapping
- while string.len(sText) > 0 do
- local whitespace = string.match( sText, "^[ \t]+" )
- if whitespace then
- -- Print whitespace
- term:write( whitespace )
- x,y = term:getCursorPos()
- sText = string.sub( sText, string.len(whitespace) + 1 )
- end
- local newline = string.match( sText, "^\n" )
- if newline then
- -- Print newlines
- newLine()
- sText = string.sub( sText, 2 )
- end
- local text = string.match( sText, "^[^ \t\n]+" )
- if text then
- sText = string.sub( sText, string.len(text) + 1 )
- if string.len(text) > w then
- -- Print a multiline word
- while string.len( text ) > 0 do
- if x > w then
- newLine()
- end
- term:write( text )
- text = string.sub( text, (w-x) + 2 )
- x,y = term:getCursorPos()
- end
- else
- -- Print a word normally
- if x + string.len(text) - 1 > w then
- newLine()
- end
- term:write( text )
- x,y = term:getCursorPos()
- end
- end
- end
- return nLinesPrinted
- end
- function printError(term,s)
- if term:isColour() then
- t.term:setTextColour( colours.red )
- end
- gui.write(term,s)
- term:setTextColour( colours.white )
- end
- local function property(class, attribute,field,setter,getter)
- class['set'..attribute]=setter or function(self,val) self[field]=val end
- class['get'..attribute]=getter or function(self) return self[field] end
- end
- local cc_events={
- mouse={
- 'mouse_click',
- 'mouse_scroll',
- 'mouse_drag',
- },
- touch={
- 'monitor_touch',
- },
- keyboard={
- 'key',
- 'char',
- },
- }
- class.Terminal()
- do
- function Terminal:__init(term,pin,pout)
- check('Term,Pipe,Pipe',term,pin,pout)
- self.term=term
- debug.namefield(self,'term')
- self.pin=pin
- debug.namefield(self,'pin')
- self.pout=pout
- debug.namefield(self,'pout')
- sched.task(sched.pump):run(pout,function(r) self:write(tostring(r)) end)
- end
- function Terminal.from_native(term,emitter)
- check('native_terminal,!nil',term,emitter)
- local h=sched.sigpipe()
- if term.isColor() then
- h:link{[emitter]=cc_events.mouse}
- end
- if term.setTextScale then
- h:link{[emitter]=cc_events.touch}
- else
- h:link{[emitter]=cc_events.keyboard}
- end
- local t=Terminal(Term.from_native(term),h.Pipe,sched.pipe())
- t.renderer=sched.task(function()
- while true do
- t.term:blit_native(term)
- t.term:copyState(term)
- sched.wait(false)
- end
- end)
- t.renderer:link{[t.term]={'*'}}
- debug.namefield(t,'renderer')
- t.renderer:run()
- t.term:setSignals(true)
- t.term:clear()
- return t
- end
- function Terminal:write(...)
- return write(self.term,...)
- end
- function Terminal:read(...)
- return read(self.pin,self.term,...)
- end
- end
- ]===================],"CoolisOS/bin/kernel/gui/Point.lua",[===================[include'kernel.class'
- local check=require'kernel.checker'.check
- --[[-------------------------------------------------------
- Members
- Point.x -- number
- Point.y -- number
- Methods
- Point:Point(x, y)
- Point:Point(point)
- Point:assign(x, y)
- Point:assign(point)
- Point:clone() -- return Point
- Point:add(x, y)
- Point:add(point)
- Point:sub(x, y)
- Point:sub(point)
- Point:equal(point)
- --------------------------------------------------------------------------]]
- class.Point()
- function Point:__init(x, y)
- return self:assign(x, y)
- end
- function Point:assign(x, y)
- if (not y) then
- check('Point',x)
- self.x = x.x
- self.y = x.y
- else
- check('number,number',x,y)
- self.x = x
- self.y = y
- end
- return self
- end
- function Point:clone()
- return Point(self)
- end
- function Point:add(x, y)
- if (not y) then
- check('Point',x)
- self.x = self.x + x.x
- self.y = self.y + x.y
- else
- check('number,number',x,y)
- self.x = self.x + x
- self.y = self.y + y
- end
- return self
- end
- function Point:sub(x, y)
- if (not y) then
- check('Point',x)
- return self:add(-x.x, -x.y)
- else
- check('number,number',x,y)
- return self:add(-x, -y)
- end
- end
- function Point:equal(p)
- check('Point',p)
- return self.x == p.x and self.y == p.y
- end
- function Point:__tostring()
- return '(' .. self.x .. ',' .. self.y ..')'
- end
- return Point
- ]===================],"CoolisOS/bin/kernel/gui/Rectangle.lua",[===================[include'kernel.class'
- local Point=require'kernel.gui.Point'
- class.Rectangle()
- --[[ -------------------------------------------------------
- Members:
- Rectangle.s -- Point (start)
- Rectangle.e -- Point (end)
- Methods:
- Rectangle:assign(x1, y1, x2, y2)
- Rectangle:assign(rect)
- Rectangle:size() -- return Point
- Rectangle:clone() -- return Rectangle
- Rectangle:move(deltax, deltay)
- Rectangle:grow(deltax, deltay)
- Rectangle:intersect(rect)
- Rectangle:union(rect)
- Rectangle:equal(rect)
- Rectangle:contains(point)
- Rectangle:empty()
- Rectangle:nempty()
- --------------------------------------------------------------------------]]
- function Rectangle:__init(x1, y1, x2, y2)
- self:assign(x1, y1, x2, y2)
- end
- function Rectangle:assign(x1, y1, x2, y2)
- if (not y1) then
- self.s = x1.s:clone()
- self.e = x1.e:clone()
- elseif (not x2) then
- self.s=x1:clone()
- self.e=y1:clone()
- else
- self.s = Point(x1, y1)
- self.e = Point(x2, y2)
- end
- return self
- end
- function Rectangle:size()
- return self.e:clone():sub(self.s)
- end
- function Rectangle:clone()
- return Rectangle(self)
- end
- function Rectangle:move(deltax, deltay)
- self.s:add(deltax, deltay)
- self.e:add(deltax, deltay)
- return self
- end
- function Rectangle:grow(deltax, deltay)
- self.s:sub(deltax, deltay)
- self.e:add(deltax, deltay)
- return self
- end
- function Rectangle:intersect(r)
- local s, e = self.s, self.e
- s.x = max(s.x, r.s.x)
- s.y = max(s.y, r.s.y)
- e.x = min(e.x, r.e.x)
- e.y = min(e.y, r.e.y)
- return self
- end
- function Rectangle:union(r)
- local s, e = self.s, self.e
- s.x = min(s.x, r.s.x)
- s.y = min(s.y, r.s.y)
- e.x = max(e.x, r.e.x)
- e.y = max(e.y, r.e.y)
- return self
- end
- function Rectangle:equal(r)
- return self.s:equal(r.s) and self.e:equal(r.e)
- end
- function Rectangle:contains(x, y)
- return x >= self.s.x and x < self.e.x and y >= self.s.y and y < self.e.y
- end
- function Rectangle:empty()
- return self.s.x >= self.e.x or self.s.y >= self.e.y
- end
- function Rectangle:nempty()
- return not self:empty()
- end
- function Rectangle:__tostring()
- if (self:empty()) then
- return 'rect (empty)'
- else
- return 'rect (' .. tostring(self.s) .. ',' .. tostring(self.e) .. ')'
- end
- end
- return Rectangle]===================],"CoolisOS/bin/kernel/gui/shell.lua",[===================[include'kernel.gui'
- local check=require'kernel.checker'.check
- local acpl=require'kernel.gui.autocomplete'
- local Box=require'kernel.class.Box'
- --[[
- methods:
- loop
- execute string->command->stuff
- runFile
- runLua
- destroy
- fields:
- cwd
- tasks
- env
- input history
- output history
- pin events
- pcmd commands
- defaults:
- cwd
- path
- Full craftos programs support:
- special option:
- ]]
- function new()
- local shell={}
- shell.env=setmetatable({},{__index=_BOX.env})
- shell.env.shell=shell
- local prompts={
- normal='>',
- }
- --[[
- input
- pout output
- ]]
- --filesystem
- shell.cwd='/'
- shell.path=fs.combine(_INSTALL_PATH,'bin/programs')
- function shell.resolve(sPath)
- check('string',sPath)
- local sStartChar = string.sub( sPath, 1, 1 )
- if sStartChar == "/" or sStartChar == "\\" then
- return fs.combine( "", sPath )
- else
- return fs.combine( shell.cwd, sPath )
- end
- end
- ----intertask
- shell.pin=sched.pipe()
- --rendering
- local w,h=term:getSize()
- local up_term=Term{size=Point(w,1)}
- local down_term=Term{size=Point(w,h-1)}
- up_term:setSignals(true)
- down_term:setSignals(true)
- -- debug.name(up_term,'up_term')
- -- debug.name(down_term,'down_term')
- sched.task(function()
- sched.running:link{[up_term]={'*'},[down_term]={'*'}}
- repeat
- up_term:blit(term)
- down_term:blit(term,Point(1,2))
- sched.wait(false)
- until false
- end):run()
- --piping stuff to proper terms
- local up_pin=_BOX.terminal.pin
- local function _read()
- up_term:clear()
- up_term:setCursorPos(1,1)
- gui.write(up_term,shell.mode..shell.prompt)
- return gui.read(up_pin,up_term,{clear=false,history=shell.input,auto={
- function(s) return acpl.getmatch(s,{env=shell.env,
- matchers={
- acpl.keyword_matcher,
- acpl.env_matcher,
- },
- }
- )
- end,
- function(s) return acpl.fs_matcher(s,{cwd=shell.cwd}) end,
- mode=shell.mode=='=' and 1 or 2,
- }
- }
- )
- end
- sched.task(function() sched.pump(nil,function() sched.wait() return _read() end,shell.pin) end):run()
- sched.task(sched.pump):run(shell.pin,function(s)
- shell.execute(s)
- end)
- shell.pout=sched.pipe()
- sched.task(sched.pump):run(shell.pout,function(s)
- gui.write(down_term,s..'\n')
- end)
- shell.perr=sched.pipe()
- sched.task(sched.pump):run(shell.perr,function(s)
- log('shell','error',s)
- gui.printError(down_term,'perr:'..s..'\n')
- end)
- --keeping track of running tasks
- shell.tasks=sched.Catalog()
- shell.trace=false
- function shell.runFunc(f,trace,stat,path)
- -- local env=setmetatable({},{__index=shell.env})
- local box=Box{pout=shell.pout,perr=shell.perr,env=shell.env}
- local func=function() return box:run(f) end
- local t=sched.task(function()
- if trace==nil then trace=shell.trace end
- local t =trace and { xpcall(func,debug.traceback) } or { pcall(func) }
- if t[1] then
- table.remove(t,1)
- table.insert(shell.output,t)
- shell.env._=t[1]
- local n=table.maxn(t)
- if n~=0 or stat then
- s=tostring(t[1])
- for i=2,n do
- s=s..','..tostring(t[i])
- end
- shell.pout:send(s)
- end
- else
- shell.perr:send(t[2] or 'error(no msg)')
- end
- end)
- debug.name(t,'in'..#shell.input)
- shell.tasks:register(t,'in'..#shell.input)
- t:run()
- end
- function shell.runFile(path)
- local f,e=io.open(path,'r')
- if not f then shell.perr:send(e or 'could not open file')
- else
- local st=f:read'*a'
- f:close()
- local f,e=loadstring(st,path)
- if not f then shell.perr:send(e)
- else
- shell.runFunc(f,true,nil,path)
- end
- end
- end
- shell.input={}
- shell.output=setmetatable({},{__index=function(t,k) return unpack(rawget(t,k)) end})
- shell.env._in=shell.input
- shell.env._out=shell.output
- shell.prompt=prompts.normal
- shell.mode='='
- local modes={
- ['>']=function(s,name) --filesystem mode
- s=shell.resolve(s)
- shell.runFile(s)
- end,
- ['=']=function(s,name)
- local proper_loadstring=function(s,n)
- check('string,string',s,n)
- local ok,f,e=pcall(loadstring,s,n)
- if e then e=e:match'(%d:[^:]-)$' end
- return f,e
- end
- local function get(s,name)
- --xpcall catches parsing errors which normally are returned as nil, error_msg.
- --so we pcall loadstring to avoid loosing the error
- --strangely, the xpcall lets execution continue at this level
- local func,e
- local f1,e1=proper_loadstring(s,name)
- local f2, e2 = proper_loadstring( "return "..s,name)
- if f1 then
- if f2 then
- func=f2
- else
- func=f1
- end
- else
- if f2 then
- func=f2
- else
- e=e1
- end
- end
- if e then e=name..':'..e end
- return func,e,(func==f2)
- end
- local func,e,stat=get(s,name)
- if func then
- shell.runFunc(func,nil,stat)
- else
- shell.perr:send(e)
- end
- end
- }
- --history
- function shell.execute(s)
- table.insert(shell.input,s)
- local name='in['..#shell.input..']'
- shell.pout:send(name..':'..s)
- if modes[s:sub(1,1)] then--mode change
- shell.mode=s:sub(1,1)
- s=s:sub(2)
- end
- if #s>0 then modes[shell.mode](s,name) end
- end
- function shell.run_startups()
- local dir=fs.combine(_INSTALL_PATH,'ect/startups')
- if not fs.exists(dir) then fs.makeDir(dir) end
- local st=fs.list(dir)
- table.sort(st)
- for i,l in pairs(st) do
- shell.runFile(fs.combine(dir,l))
- end
- end
- _BOX.env.shell=shell
- return shell
- end
- ]===================],"CoolisOS/bin/kernel/gui/Term.lua",[===================[include'kernel.class'
- local Opt=require'kernel.class.Opt'
- local checker=require'kernel.checker'
- local check=checker.check
- local Point=require'Point'
- local Rectangle=require'Rectangle'
- -- local function nimpl() return error('Not implemented',3) end
- -- class.DC()
- -- function DC:blit(pos,section) nimpl() end
- -- function DC:setCursorPos(pos,section) nimpl() end
- -- function DC:write(txt) nimpl() end
- -- function DC:setTextColor(pos,section) nimpl() end
- -- function DC:setBackgroundColor(pos,section) nimpl() end
- class.Term()
- local term_opt=Opt{
- size=Opt(Point(5,5)),
- bcolor=colors.black,
- tcolor=colors.white,
- pos=Opt(Point(1,1)),
- color=term.isColor(),
- blink=false,
- }
- local function update(self) self:emit(UPDATE) end
- checker.custom.color=function(n) return (type(n)=='number' and n>=0 or error('Invalid color, must be number >0',3)) end
- checker.custom.char=function(s) return type(s)=='string' and #s==1 end
- local native=util.shcopy(term.native)
- checker.custom.native_terminal=function(t)
- for i,v in pairs(native) do
- if type(t[i])~=type(native[i]) then return false end
- end
- if typeof(t)~='table' then return false end
- return true
- end
- function Term.from_native(term)
- check('native_terminal',term)
- return Term{
- size=Point(term.getSize()),
- color=term.isColor(),
- blink=true,
- }
- end
- local function wrap_obj(self,tMethods)
- for i,v in pairs(tMethods or self) do
- if type(v)=='function' then
- local f=self[i]
- self[i]=function(s,...)
- if self==s then
- return f(s,...)
- else
- return f(self,s,...)
- end
- end
- end
- end
- end
- --[[process
- e.g. shell
- in out err
- term
- ---in out in out
- ]]
- local active={
- 'setBackgoundColour',
- 'setTextColour',
- 'setCursorPos',
- 'setCursorBlink',
- 'write',
- 'setSize',
- 'scroll',
- 'clear',
- 'setPixel',
- }
- function Term:setSignals(bool)
- for _,k in ipairs(active) do
- local f=self['_'..k] or self[k]
- self[k]=bool and function(...)
- sched.signal(self,k,...)
- return f(...)
- end or f
- end
- end
- function Term:__init(opt)
- term_opt:drop(self,opt)
- self:_allocate()
- wrap_obj(self,native)
- end
- function Term:setCursorPos(x,y) self.pos:assign(x,y) end
- function Term:getCursorPos() return self.pos.x,self.pos.y end
- function Term:setBackgroundColor(color) check('color',color) self.bcolor=color end
- Term.setBackgroundColour=Term.setBackgroundColor
- function Term:getBackgroundColor() return self.bcolor end
- Term.getBackgroundColour=Term.getBackgroundColor
- function Term:setTextColor(color) check('color',color) self.tcolor=color end
- Term.setTextColour=Term.setTextColor
- function Term:getTextColor() return self.tcolor end
- Term.getTextColour=Term.getTextColor
- function Term:isColor() return self.color end
- Term.isColour=Term.isColor
- function Term:setCursorBlink(bool) check('boolean',bool) self.blink=bool end
- function Term:getCursorBlink() return self.blink end
- function Term:write(txt)
- check('string',txt)
- local n=#txt
- local pos=self.pos
- if pos.x+n<=1 or pos.x> self.size.x then
- pos.x=pos.x+n
- return
- end
- if pos.x < 1 then
- txt=txt:sub(math.abs(pos.x)+2)
- pos.x=1
- end
- local line=self.matrix[pos.y]
- if line then
- for j=pos.x,math.min(pos.x-1+n,self.size.x) do
- line[j]={tc=self.tcolor,bc=self.bcolor,c=txt:sub(j-pos.x+1,j-pos.x+1)}
- end
- end
- pos.x=pos.x+n
- end
- function Term:setSize(self,size)
- self.size=size
- self:_allocate()
- end
- function Term:getSize()
- return self.size.x,self.size.y
- end
- function Term:scroll(n)
- check('number',n)
- n=(n-n%1)
- local ind,nind=n>0 and 1 or nil,n>0 and self.size.y or 1
- local matrix=self.matrix
- for i=1,math.abs(n) do
- table.remove(matrix,ind)
- matrix[nind]=self:_newLine()
- end
- end
- function Term:clear() self:_allocate() end
- function Term:clearLine()
- if self.matrix[self.pos.y] then
- self.matrix[self.pos.y]=self:_newLine()
- end
- end
- function Term:_newLine()
- local line={}
- for j=1,self.size.x do
- line[j]={tc=self.tcolor,bc=self.bcolor,c=' '}
- end
- return line
- end
- function Term:_allocate()
- local X,Y = self.size.x,self.size.y
- self.matrix=self.matrix or {}
- for i=1,Y do
- self.matrix[i]=self:_newLine()
- end
- end
- local def_pos=Point(1,1)
- function Term:blit_native(term,pos,section)
- pos=pos or def_pos
- section=section or Rectangle(def_pos,self.size)
- term.setCursorBlink(false)
- if self.blink then
- local cell=self.matrix[self.pos.y] and self.matrix[self.pos.y][self.pos.x]
- if cell then
- self._bcell=cell
- cell.bc,cell.tc=cell.tc,cell.bc
- else
- self._bcell=nil
- end
- end
- for i=section.s.y,section.e.y do
- local line=self.matrix[i]
- local tc,bc=line[1].tc,line[1].bc
- local tTxt={}
- for j=section.s.x,section.e.x do
- local tl,bl=line[j].tc,line[j].bc
- if tc~=tl or bc~=bl then
- term.setCursorPos(j-#tTxt+pos.x-1,i+pos.y-1)
- term.setTextColor(tc==0 and self.tcolor or tc)
- term.setBackgroundColor(bc==0 and self.bcolor or bc)
- term.write(table.concat(tTxt))
- tc,bc=tl,bl
- tTxt={}
- end
- table.insert(tTxt,line[j].c)
- end
- if tTxt[1] then
- term.setCursorPos(section.e.x-#tTxt+pos.x,i+pos.y-1)
- term.setTextColor(tc==0 and self.tcolor or tc)
- term.setBackgroundColor(bc==0 and self.bcolor or bc)
- term.write(table.concat(tTxt))
- end
- end
- if self._bcell then
- local cell=self._bcell
- cell.bc,cell.tc=cell.tc,cell.bc
- self._bcell=nil
- end
- end
- function Term:blit(term,pos,section)
- return self:blit_native(term,pos,section) --temporary?
- end
- function Term:setPixel(char,tcolor,bcolor,x,y)
- check('char,?|color,?|color,?|Point|number|table,?|number',char,tcolor,bcolor,x,y)
- if type(x)=='table' then
- x,y=x.x,x.y
- check('number,number',x,y)
- else
- x,y=x or self.pos.x, y or self.pos.y
- end
- if self.matrix[y] and self.matrix[y][x] then
- self.matrix[y][x]={c=char or ' ',tc=tcolor or self.tcolor,bc=bcolor or self.bcolor}
- end
- end
- function Term:copyState(term)
- term.setTextColor(self:getTextColor())
- term.setBackgroundColor(self:getBackgroundColor())
- term.setCursorPos(self:getCursorPos())
- end
- -- function Term:from_npaint(file)
- -- -- pos=pos or def_pos
- -- -- section=section or Rectangle(pos,self.size)
- -- for line in file:lines() do
- -- if section.x<
- -- end
- -- end
- function Term:_dump_key(k)
- local s={}
- for i=1, self.size.y do
- local txt={}
- table.foreach(self.matrix[i],function(_,v) table.insert(txt,tostring(type(v[k])=='number' and string.char(97+math.ceil(math.log(v[k],2))) or v[k])) end)
- txt=table.concat(txt)
- table.insert(s,txt)
- end
- return table.concat(s,'\n')
- end
- function Term:dump()
- return self:_dump_key'c',self:_dump_key'tc',self:_dump_key'bc'
- end
- function Term:blit_all(term)
- for i=1,self.size.y do
- local line=self.matrix[i]
- for j=1,self.size.x do
- local p=line[j]
- term.setCursorPos(j,i)
- term.setTextColor(p.tc)
- term.setBackgroundColor(p.bc)
- term.write(p.c)
- end
- end
- end
- -- assert(Term:implements(DC))
- Term.test=function()
- rawset(_G,'t',Term{size=Point(term.getSize())})
- for i=1,t.size.y do
- t:setPixel('a',2^i,2^i,i,i)
- end
- sleep(1)
- term.clear()
- -- t:setCursorPos(2,2)
- t:blit_native(term)
- sleep(1)
- end
- return Term]===================],"CoolisOS/bin/kernel/loadreq",true,"CoolisOS/bin/kernel/loadreq/init.lua",[===================[---require implementation in pure Lua for a sandbox environment, with support for globs and full directory tree search.
- --Licensed under the MIT license, whatever that is.
- local fs=fs
- local string=string
- local loadfile=loadfile
- local error=error
- local function log(...)
- return (_G.log and _G.log(...))
- end
- function getNameExpansion(s) --returns name and expansion from a filepath @s; special cases: ''->'',nil; '.'-> '','';
- local _,_,name,expa=string.find(s, '([^%./\\]*)%.(.*)$')
- return name or s,expa
- end
- function getDir(s) --returns directory from filepath @s
- return string.match(s,'^(.*)/') or '/'
- end
- vars={} --to allow edition
- --vars.loaded={}
- --function loadFile(path,_reload) --@_reload forces reload
- -- if (vars.loaded[path]==nil) or _reload then
- -- local fnFile, err = loadfile( path )
- -- if not fnFile then error('load:'..'path=:'..path..'| '..(err or 'nil'),2) end
- -- vars.loaded[path]=fnFile
- -- end
- -- return vars.loaded[path]
- --end
- --
- ---iterator; replaces '?' in g by s and returns the resulting path if it is a file.
- local function direct(g,s)
- g=string.gsub(g,'%?',s)
- if fs.exists(g) and not fs.isDir(g) then
- return g
- end
- end
- --add your finders here: must take (p,s) where p is where to search and s is what to search for.It must return a path.
- vars.finders={direct}
- vars.paths='?/init.lua;?.lua'
- --helper vars for lua_requirer; other requirers may index theirs vars in loadreq.vars
- vars.lua_requirer={
- required={}, --to unrequire filepath s do require[s]=nil
- required_envs={}, --to prevent garbage collection
- requiring={}, --to throw error in case of recursive requiring;
- }
- --[[lua_requirer(path,cenv,env,renv,rerun,args)
- Accepts empty or .lua extensions.
- if the rerun flag is true, reloads the file even if it done it before;
- if the file has been loaded already returns previous value;
- if the file is being loaded returns nil, error_message
- else:
- loads file in @path;
- sets it's env to @env, default {} with metatable with __index set to @renv, default _G;
- calls the function with unpack(@args) and returns and saves either
- the function return
- a shallow copy of the functions environment
- ]]
- function lua_requirer(path,cenv,t)
- local env,renv,rerun,args=t.env,t.renv,t.rerun,t.args
- local err_prefix='lua_requirer:'
- local vars=vars.lua_requirer
- local _,ext=getNameExpansion(path)
- if not (ext=='' or ext=='lua' or ext==nil) then
- return nil, err_prefix..'wrong extension:'..ext
- end
- if vars.requiring[path] then
- return nil, err_prefix..'file is being loaded'
- end
- if not rerun and vars.required[path] then
- return vars.required[path]
- end
- local f,e=loadfile(path,path)
- if not f then
- return nil,err_prefix..'loadfile:'..e
- end
- env=env or {}
- env._FILE_PATH=path
- vars.required_envs[path]=env
- setfenv(f,env)
- renv=renv or _G
- setmetatable(env,{__index=renv})
- vars.requiring[path]=true
- local ok,r=pcall(f,args)
- vars.requiring[path]=nil
- if not ok then
- return nil,err_prefix..'while calling module:\n'..r
- elseif r then
- vars.required[path]=r
- return r
- else
- local t={}
- for i,v in pairs(env) do
- t[i]=v
- end
- vars.required[path]=t
- return t
- end
- end
- --replacement for os_loadAPI that keeps APIS to the caller's env
- --local old_os_loadAPI=os.loadAPI
- --new_os_loadAPI=function(p)
- -- local cenv=getfenv(2)
- -- local r,e=loadreq.lua_requirer(p,cenv)
- -- if r then
- -- local name=getNameExpansion(p)
- -- protect(r)
- -- cenv[p]=r
- -- else
- -- error('os.loadAPI(loadreq version):'..e..'\n'..'path='..p,2)
- -- end
- --end
- --[[add your requirers here;
- each must
- take as arguments (path,cenv,...) where
- path is the path to required file
- cenv is the environment of the caller of @require
- ... are extra arguments passed to @require
- return
- one value to be returned by @require
- false|nil, error_message in case of failure
- ]]
- vars.requirers={lua=lua_requirer}
- function sufix(s)
- return string.gsub('@/?;@/?.lua;@/?/init.lua;@/?/?.lua;@/?/?;@','@',s)
- end
- local function _find(s,paths,caller_env)
- local err={'_find: finding '..tostring(s)}
- if paths then
- elseif caller_env.REQUIRE_PATH then
- paths=caller_env.REQUIRE_PATH
- elseif caller_env.PACKAGE_NAME and caller_env._FILE_PATH then
- paths=sufix(string.match(caller_env._FILE_PATH,'^(.-'..caller_env.PACKAGE_NAME..')'))..';'..vars.paths
- elseif caller_env._FILE_PATH then
- paths=sufix(getDir(caller_env._FILE_PATH))..';'..vars.paths
- else
- paths=vars.paths
- end
- --replace . by / and .. by .
- s=string.gsub(s,'([^%.])%.([^%.])','%1/%2')
- s=string.gsub(s,'^%.([^%.])','/%1')
- s=string.gsub(s,'%.%.','.')
- local finders=vars.finders
- local finder,path
- for i=1,#finders do
- finder=finders[i]
- for search_path in string.gmatch(paths,';?([^;]+);?') do
- path=finder(search_path,s)
- if path then return path end
- end
- end
- table.insert(err,'_find:file not found:'..s..'\ncaller path='..(caller_env._FILE_PATH or 'not available'))
- local serr=table.concat(err,'\n')
- log('loadreq','ERROR','_find:%s',serr)
- return nil,serr
- end
- find=_find
- local function _require(s,opt,caller_env)
- local err={}
- table.insert(err,'loadreq:require: while requiring '..tostring(s))
- local path,e=_find(s,opt.path,caller_env)
- if path==nil then
- table.insert(err,e)
- return nil, table.concat(err,'\n')
- end
- for req_name,requirer in pairs(vars.requirers) do
- local r,e=requirer(path,caller_env,opt)
- if r then
- return r,path
- else
- table.insert(err,e)
- end
- end
- return nil, table.concat(err,'\n')
- end
- --[[require(s,paths,...)
- @paths is a string of paths separated by ';' where there can be '?'
- -acquires @paths variable, by the following order;
- 0-arg @paths
- Example (_FILE_PATH='myFolder/myFolder2/myAPI.lua'):
- myAPI=require('myFolder2.myAPI','myFolder/?.lua')
- 1-REQUIRE_PATH in the caller's path, if existent
- Example (_FILE_PATH='myFolder/myFolder2/myAPI.lua'):
- REQUIRE_PATH='myFolder/?.lua'
- myAPI=require'myFolder2.myAPI'
- 2-directory named PACKAGE_NAME in _FILE_PATH, if defined in the caller's environment
- with sufixes appended by @sufix and concatenated with @vars.paths.
- _FILE_PATH is set, for instance, by lua_loader in the files it loads.
- Example (_FILE_PATH='myFolder/myFolder3/myFolder/runningFile'):
- PACKAGE_NAME='myFolder'
- myAPI=require'myAPI' --@paths is 'myFolder/?;myFolder/?.lua;myFolder/?/init.lua;myFolder/?/?.lua;myFolder/?/?;myFolder'
- 3-directory of _FILE_PATH, if defined
- with sufixes appended by @sufix and concatenated with @vars.paths.
- Example (_FILE_PATH='myFolder/runningFile'):
- myAPI=require'myAPI' --@paths is 'myFolder/?;myFolder/?.lua;myFolder/?/init.lua;myFolder/?/?.lua;myFolder/?/?;myFolder'
- [email protected] as set in loadreq.vars.paths
- -replaces '.' in @s by '/' and '..' by '.'
- --for all search_path in @paths
- - for all iterators in vars.finders, iterates over the paths returned;
- default iterator: see @direct
- -for the first valid path, calls the loaders sequentially until one succeds,
- in which case it returns the first value that the loader returns, else if it returns nil,e accumulates e as an error message
- els, if all loaders fail, errors immediatly, printing all error messages
- -in case of failure finding the path, errors with useful info
- ]]
- function require(s,opt)
- opt=opt or {}
- local t,e=_require(s,opt,getfenv(2))
- if t==nil then
- log('loadreq','ERROR','require:%s',e)
- error(e,1)
- else
- log('loadreq','INFO','require: success in requiring [%s]',e)
- return t
- end
- end
- ---same as require, but copies the returned API to the caller's environment
- function include(s,opt)
- opt=opt or {}
- local caller_env=getfenv(2)
- local t,e=_require(s,opt,caller_env)
- if t then
- for i,v in pairs(t) do
- if type(i)=='string' and i:sub(1,1)~='_' then
- caller_env[i]=v
- end
- end
- return true
- else
- log('loadreq','ERROR','include:%s',e)
- error(e,1)
- end
- end
- function reload(s,opt)
- opt=opt or {}
- opt.rerun=true
- return require(s,opt)
- end
- --protection utilities; these are meant more as a warning than OS-grade protection
- vars.bProtected=true
- local function protected_access(t,k,v)
- if vars.bProtected then
- error( "Attempt to write to protected" )
- else
- rawset( t, k, v )
- end
- end
- function protect(_t)--shallow protection for tables (slows down access to the tables themselves, not to their values, e.g. local t.a=protected_table.a print(a) should do the trick)
- local meta = getmetatable( _t )
- if meta == "Protected" then
- -- already hard protected
- return
- end
- if meta then
- meta.__newindex = protected_access
- else
- setmetatable( _t, {__newindex = protected_access} )
- end
- end
- function unprotect(_t)--removes the shallow protection
- local meta = getmetatable( _t )
- if not meta then
- return
- end
- vars.bProtected=false
- meta.__newindex=nil
- vars.bProtected=true
- end
- function permaProtect(_t) --same as the os.loadAPI does
- local meta = getmetatable( _t )
- if meta == "Protected" then
- -- already hard protected
- return
- end
- setmetatable( _t, {
- __newindex = function( t, k, v )
- if vars.bProtected then
- error( "Attempt to write to protected" )
- else
- rawset( t, k, v )
- end
- end,
- __metatable = 'Protected',
- } )
- end
- env=getfenv()
- return env
- ]===================],"CoolisOS/bin/kernel/log",true,"CoolisOS/bin/kernel/log/init.lua",[===================[------------------------------------------------------------------------------
- -- Log library provides logging facilities.
- -- The module exposes extension points. It is possible to provide both the custom printing function and the custom log saving function. <br />
- -- The module is callable. Thus:
- -- local log = require"log"
- -- log("MODULE", "INFO", "message")
- -- calls the @{#log.trace} function.
- -- @module log
- --
- local string,pcall,table,tostring,setmetatable,os,pairs,next,print,math,type,unpack=
- string,pcall,table,tostring,setmetatable,os,pairs,next,print,math,type,unpack
- local base = _G
- local util=require'kernel.util'
- local check =require'kernel.checker'.check
- local textutils=textutils
- local string=string
- --temporary
- local pprint=pprint
- --no more global access
- local env=getfenv()
- setmetatable(env,nil)
- function s_to_real(t)
- local n_s=t%60
- local m=((t-n_s)/60)
- local n_m=m%60
- local h=((m-n_m)/60)
- return h,n_m,n_s
- end
- local os_time= function()
- local mc='Day '..os.day()..', '..textutils.formatTime(os.time())
- local t=os.clock()
- local h,n_m,n_s=s_to_real(t)
- local sec=tostring(n_s)
- local s,p,e=sec:match('^([^%.]*)(%.)([^%.]*)$')
- s=s or '0'
- p=p or '.'
- e=e or '0'
- s=string.rep('0',2-s:len())..s
- e=e..string.rep('0',2-e:len())
- sec=s..p..e
- local real=string.format('%sh%sm%ss',h,n_m,sec)
- return string.format('%s',sec)--string.format('%s;%s;%s',mc,real,sec)
- end
- -------------------------------------------------------------------------------
- -- Log levels are strings used to filter logs.
- -- Levels are to be used both in log filtering configuration (see @{#log.setLevel}) and each time
- -- @{#log.trace} function is used to issue a new log.
- --
- -- Levels are ordered by verbosity/severity level.
- --
- -- While configuring log filtering, if you set a module log level to 'INFO' level for exemple, you
- -- enable all logs *up to* 'INFO', that is to say that logs with 'WARNING' and 'ERROR' severities will
- -- be displayed too.
- --
- -- Built-in values (in order from the least verbose to the most):
- -- - 'NONE': filtering only: when no log is wanted
- -- - 'ERROR': filtering or tracing level
- -- - 'WARNING': filtering or tracing level
- -- - 'INFO': filtering or tracing level
- -- - 'DETAIL': filtering or tracing level
- -- - 'DEBUG': filtering or tracing level
- -- - 'ALL': filtering only: when all logs are to be displayed
- -- @type levels
- --
- levels = {}
- -- Severity name <-> Severity numeric value translation table (internal purpose only)
- for k,v in pairs{ 'NONE', 'ERROR', 'WARNING', 'INFO', 'DETAIL', 'DEBUG', 'ALL' } do
- levels[k], levels[v] = v, k
- end
- -- -----------------------------------------------------------------------------
- -- Default verbosity level.
- -- Default value is `"WARNING"`.
- -- @field [parent=#log] #levels defaultlevel
- -- See @{#log} for a list of existing levels.
- local defaultlevel = defaultlevel or levels.WARNING
- -- -----------------------------------------------------------------------------
- -- Per module verbosity levels.
- -- @field [parent=#log] modules
- -- See @{#levels} to see existing levels.
- local modules = modules or { }
- -- -----------------------------------------------------------------------------
- -- The string format of the timestamp is the same as what os.date takes.
- -- Example: "%F %T"
- -- #field [parent=#log] #string timestampformat
- timestampformat = nil
- -- -----------------------------------------------------------------------------
- -- logger functions, will be called if non nil
- -- the loggers are called with following params (module, severity, logvalue)
- -- -----------------------------------------------------------------------------
- -------------------------------------------------------------------------------
- -- Default logger for instant display.
- -- This logger can be replaced by a custom function.
- -- It is called only if the log needs to be traced.
- --
- -- @function [parent=#log] displaylogger
- -- @param #string module string identifying the module that issues the log
- -- @param severity string representing the log level, see @{log#levels}.
- -- @param msg string containing the message to log.
- --
- displaylogger = function(_, _, ...)
- -- if base.print then base.print(...) end
- end
- -------------------------------------------------------------------------------
- -- Logger function for log storage.
- -- This logger can be replaced by a custom function.
- -- There is no default store logger.
- -- It is called only if the log needs to be traced (see @{#log.musttrace}) and after the log has been displayed using {displaylogger}.
- --
- -- @function [parent=#log] storelogger
- -- @param module string identifying the module thats issues the log
- -- @param severity string representing the log level, see @{log#levels}.
- -- @param msg string containing the message to log.
- --
- storelogger = nil
- -------------------------------------------------------------------------------
- -- Format is a string used to apply specific formating before the log is outputted.
- -- Within a format, the following tokens are available (in addition to standard text)
- --
- --- %l => the actual log (given in 3rd argument when calling log() function)
- --- %t => the current date
- --- %m => module name
- --- %s => log level (severity), see @{log#levels}
- --@field [parent=#log] #string format
- format = nil
- local function loggers(...)
- if displaylogger then displaylogger(...) end
- if storelogger then storelogger(...) end
- end
- -------------------------------------------------------------------------------
- -- Determines whether a log must be traced depending on its severity and the
- -- module issuing the log. This function is mostly useful to protect `log()`
- -- calls which involve costly computations
- --
- -- @usage
- --
- -- if log.musttrace('SAMPLE', 'DEBUG') then
- -- log('SAMPLE', 'DEBUG', "Here are some hard-to-compute info: %s",
- -- computeAndReturnExpensiveDebugString())
- -- end
- --
- -- @function [parent=#log] musttrace
- -- @param modulename string identifying the module that issues the log.
- -- @param severity string representing the log level, see @{log#levels}.
- -- @return `nil' if the message of the given severity by the given module should
- -- not be printed.
- -- @return `true` if the message should be printed.
- --
- function musttrace(module, severity)
- -- get the log level for this module, or default log level
- local lev, sev = modules[module] or defaultlevel, levels[severity]
- return not sev or lev >= sev
- end
- local function debug_getinfo(...)
- return (base.debug and base.debug.getinfo or tostring)(...)
- end
- -------------------------------------------------------------------------------
- -- Prints out a log entry according to the module and the severity of the log entry.
- --
- -- This function uses @{#log.format} and @{#log.timestampformat} to create the
- -- final message string. It calls @{#log.displaylogger} and @{#log.storelogger}.
- --
- -- @function [parent=#log] trace
- -- @param modulename string identifying the module that issues the log.
- -- @param severity string representing the level in @{log#levels}.
- -- @param fmt string format that holds the log text the same way as string.format does.
- -- function must return fmt (string) , vararg
- -- @param varargs additional arguments can be provided (as with string.format).
- -- @usage trace("MODULE", "INFO", "message=%s", "sometext").
- --
- function trace(module, severity, fmt, ...)
- local args
- if type(fmt)=='function' then
- local t={fmt(...)}
- fmt=table.remove(t,1)
- args=t
- else args={...} end
- check('string,string,string',module, severity, fmt)
- if not musttrace(module, severity) then return end
- local c, s = pcall(string.format,fmt, unpack(util.map_args(args,function(v) if (type(v)=='number' or type(v)=='string') then return v else return debug_getinfo(v) end end)))
- if c then
- local t
- local function sub(p)
- if p=="l" then return s
- elseif p=="t" then t = t or tostring(os_time(timestampformat)) return t
- elseif p=="m" then return module
- elseif p=="s" then return severity
- else return p end
- end
- local out = (format or "%t %m-%s: %l"):gsub("%%(%a)", sub)
- loggers(module, severity, out)
- else -- fallback printing when the formating failed. The fallback printing allow to safely print what was given to the log function, without crashing the thread !
- --trace(module, severity, "\targs=("..table.concat(args, " ")..")" )
- loggers(module, severity, "Error in the log formating! ("..tostring(s)..") - Fallback to raw printing:" )
- loggers(module, severity, string.format("\tmodule=(%s), severity=(%s), format=(%q), args=(%s)", module, severity, fmt,table.concat(util.map_args(args,debug.getinfo),',') ))
- end
- end
- -------------------------------------------------------------------------------
- -- Sets the log level for a list of module names.
- -- If no module name is given, the default log level is affected
- -- @function [parent=#log] setLevel
- -- @param slevel level as in @{log#levels}
- -- @param varargs Optional list of modules names (string) to apply the level to.
- -- @return nothing.
- --
- function setLevel(slevel, ...)
- local mods = {...}
- local nlevel = levels[slevel] or levels['ALL']
- if not levels[slevel] then
- trace("LOG", "ERROR", "Unknown severity %q, reverting to 'ALL'", tostring(slevel))
- end
- if next(mods) then for _, m in pairs(mods) do modules[m] = nlevel end
- else defaultlevel = nlevel end
- end
- ---
- local wrapped=setmetatable({},{__mode='k'})
- function wrap_class(k)
- assert(not wrapped[k])
- local old=k.__init
- rawset(k,'__init',function(...)
- old(...)
- env(tostring(k.__name),'DETAIL','initiated obj %s with args ',debug.getinfo(obj),args_tostring(...))
- end)
- wrapped[k]=old
- end
- function unwrapp_class(k)
- if wrapped[k] then
- rawset(k,'__init',wrapped[k])
- wrapped[k]=nil
- end
- end
- -- -----------------------------------------------------------------------------
- -- Make the module callable, so the user can call log(x) instead of log.trace(x)
- -- -----------------------------------------------------------------------------
- setmetatable(env, {__call = function(_, ...) return trace(...) end })
- return env
- ]===================],"CoolisOS/bin/kernel/log/store.lua",[===================[local store={}
- local log_dir=fs.combine(_INSTALL_PATH,'ect/log')
- if not fs.exists(log_dir) then
- fs.makeDir(log_dir)
- end
- local his={}
- local function flush_file(s)
- local f=fs.open(s,'a')
- for i,v in pairs(his[s]) do
- f.writeLine(v)
- end
- f.close()
- his[s]=nil
- end
- function store.flush(module)
- if not module then
- for s,t in pairs(util.shcopy(his)) do
- flush_file(s)
- end
- else
- local s=fs.combine(log_dir,module..'.log')
- flush_file(s)
- end
- end
- store.instant=false
- function store.append(module,msg)
- local s=fs.combine(log_dir,module..'.log')
- his[s]=his[s] or {}
- table.insert(his[s],msg)
- if store.instant==true then
- flush_file(s)
- end
- end
- function store.reset(module)
- if module then
- fs.open(fs.combine(log_dir,module..'.log'),'w').close()
- else
- fs.delete(log_dir)
- fs.makeDir(log_dir)
- end
- end
- log.storelogger=function(module,severity,msg)
- store.append(module,msg)
- store.append('log',msg)
- end
- return store]===================],"CoolisOS/bin/kernel/sched",true,"CoolisOS/bin/kernel/sched/barrier.lua",[===================[local check=require 'kernel.checker'.check
- PACKAGE_NAME='sched'
- local sched=require 'init'
- --------------------------------------------------------------------------------
- -- Object metatable/class
- --------------------------------------------------------------------------------
- local B={__type='barrier'};B.__index=B
- --------------------------------------------------------------------------------
- -- A registry of all currently active barriers. This is intended as a debug aid,
- -- and can be safely commented out if not needed
- --------------------------------------------------------------------------------
- sched.barriers = setmetatable({}, {__mode='kv'})
- function barrier(n)
- check ('integer',n)
- local instance={n=n}
- if sched.barriers then
- sched.barriers[tostring(instance):match(':.(.*)')]=instance
- end
- setmetatable(instance,B)
- return instance
- end
- B.reach=function(self)
- if self.n<=1 then
- self.n=0
- sched.signal(self,'zero')
- else
- self.n=self.n-1
- sched.wait(self,'zero')
- end
- end
- return barrier]===================],"CoolisOS/bin/kernel/sched/catalog.lua",[===================[--- A general purpose Catalog.
- -- The catalog is used to give tasks well known names for sharing purposes.
- -- It also allows synchronization, by blocking the requester until the object
- -- is made available. Catalogs themselves are made available under a Well Known
- -- name. Typical catalogs are "tasks", "mutexes" and "pipes".
- -- The catalog does not check for multiple names per object.
- -- @module catalog
- -- @usage local tasks = require 'catalog'()
- --...
- --tasks:register('a task', sched.Task.running)
- --...
- --local a_task=tasks:waitFor('a task')
- -- @alias M
- include'kernel.class'
- local log = require'kernel.log'
- local check=require'kernel.checker'.check
- local named=setmetatable({},{__mode='kv'})
- class.Catalog()
- local C=Catalog
- C.named=named
- function C:__init()
- self.objs=setmetatable({},{__mode='kv'})
- end
- function C:register(obj,name)
- if type(obj)~='table' then error('not a table',2) end
- if self.objs[name]~=nil and self.objs[name]~=obj then error('name taken',2) end
- self.objs[obj]=name
- self.objs[name]=obj
- sched.signal(self,'+'..name,obj)
- named[obj]=named[obj] or setmetatable({},{__mode='kv'})
- named[obj][self]=true
- end
- function C:unregister(id)
- local s = self.objs[id]
- local name=type(s)=='string' and s or id
- self.objs[s]=nil
- self.objs[id]=nil
- if named[obj] then
- named[obj][self]=nil
- end
- sched.signal(self,'-'..name,obj)
- end
- function C:waitFor(name,timeout)
- check('string',name)
- log('catalog', 'INFO', 'catalog %s queried for name "%s" by %s',self, name,sched.me())
- local obj
- if self[name] then return self[name] else
- local em,ev,o=sched.wait(self,'+'..name,timeout)
- if em==self then obj=o end
- end
- log('catalog', 'INFO', 'catalog %s queried for name "%s" by %s, found %s',self, name,sched.me(),obj)
- return obj
- end
- function C:__tostring()
- local t={}
- for i,v in pairs(self.objs) do
- if type(i)=='string' then t[i]=i..'->'..debug.getinfo(v) end
- end
- return debug.getinfo(self)..'\n'..pstring(t,1)
- end
- sched.Catalog=Catalog]===================],"CoolisOS/bin/kernel/sched/init.lua",[===================[include'kernel.class'
- local log=require'kernel.log'
- local check=require'kernel.checker'.check
- local Linked=require 'utils.linked'
- local table=table
- local pairs=pairs
- local sched,Obj,Sync,Task,timer,platform
- local os_time
- local fil
- --[[
- 2D array of sets of objs, fil[emitter][event][obj]=true
- wildcards for emitter, event are '*'; can only be used for listening
- reserved values; do not use for custom signals:
- -emitters:
- 'timer' -timer module;
- 'platform' -events yielded to the scheduler by os.pullEventRaw
- ]]
- ---emits a signal with emitter @emitter, event @event and parameters @vararg (...)
- --can be called recursively; be careful not to signal the same entry which is being run
- --to use infinite loops of signals (e.g. A->B->A) blocking the scheduler, use @Task or @Call instances.
- local signal = function (emitter,event,...)
- log('sched', 'DEBUG', "SIGNAL [%s].[%s]", emitter, event)
- local function walk_event(evl,emitter,event,...)
- local copy={}
- for obj,val in pairs(evl) do
- copy[obj]=val
- end
- for obj,val in pairs(copy) do
- -- if d then print(obj) end
- obj:handle(emitter,event,...)
- end
- end
- local function walk_emitter(eml,emitter,event,...)
- local evl=eml[event]
- if evl then
- walk_event(evl,emitter,event,...)
- end
- evl=eml['*']
- if evl then
- walk_event(evl,emitter,event,...)
- end
- end
- local eml=fil[emitter]
- if eml then
- walk_emitter(eml,emitter,event,...)
- end
- local eml=fil['*']
- if eml then
- walk_emitter(eml,emitter,event,...)
- end
- return true
- end
- --[[Class that Task,Sync implement and that describes what can be put into the waiting table @fil.
- --Objs instances keep track of which signals call their @handle field in their @fil field;
- --their @parent and @subs fields maintain a tree-like hierarchy used to destroy @subs from an object before destroying the object itself.
- --Objs expect to have defined a handle field consisting of a function used to process signals received, syncronously:
- --@handle is a function called with args (obj,emitter,event,...) everytime a signal (emitter,event,...) that obj is linked too is emitted.
- ]]
- Obj=class('Obj')
- do
- local O=Obj
- --@name is an optional parameter to set a debugging name
- function O:__init()
- self.fil={}
- self.subs={}
- if sched.running then self:setParent(sched.running) end
- end
- ---Takes a link descriptor and tranforms them into a a proper link table for Obj methods to consume
- --supports emitter,ev1,ev2,...,timeout
- --or {[emt1]={ev1,ev2,...},...},timeout
- local function get_args(...)
- local args={...}
- local nargs=#args
- local t,timeout
- if type(args[nargs])=='number' then
- timeout=args[nargs]
- nargs=nargs-1
- end
- if nargs~=0 then
- if nargs==1 and typeof(args[1])=='table' then
- t=args[1]
- else
- t={[args[1]]={nargs==1 and '*' or unpack(args,2,nargs)}}
- end
- end
- return t,timeout
- end
- --helper
- local add_timer=function(t,nd)
- t=t or {}
- if nd then
- if not t[timer] then
- t[timer]={nd}
- else
- table.insert(t[timer],nd)
- end
- end
- return t
- end
- function get_t(...)
- local t,timeout=get_args(...)
- if timeout then t=add_timer(t,timeout+os_time()) end
- return t
- end
- ---Registers @obj in the waiting table @fil in the waiting sets described by the link table @t
- function O:link(...)
- local t=get_t(...)
- if t==nil then return end
- local ofil=self.fil
- local root=self.__root
- local fil_tev,ofil_tev,fil_tobj
- for em,tev in pairs(t) do
- if type(tev)~='table' then error('Wrong struct for link',2) end
- fil_tev=fil[em]
- ofil_tev=ofil[em]
- if not ofil_tev then
- ofil[em]={}
- ofil_tev=ofil[em]
- if not fil_tev then fil[em]={} fil_tev=fil[em] end
- end
- local ev
- for i=1,#tev do
- ev=tev[i]
- ofil_tev[ev]=true
- fil_tobj=fil_tev[ev]
- if not fil_tobj then fil_tev[ev]={} fil_tobj=fil_tev[ev] end
- fil_tobj[root]=true
- end
- end
- end
- ---Unregisters @obj in the waiting table @fil from the waiting sets described by the link table @t
- function O:unlink(...)
- local r=self.__root
- local t=get_t(...)
- if t==nil then return end
- local ofil=self.fil
- local fil_tev,ofil_tev,fil_tobj
- for em,tev in pairs(t) do
- ofil_tev=ofil[em]
- if ofil_tev then
- fil_tev=fil[em]
- local ev
- for i=1,#tev do
- ev=tev[i]
- if ofil_tev[ev] then
- ofil_tev[ev]=nil
- fil_tobj=fil_tev[ev]
- fil_tobj[r]=nil
- if not next(fil_tobj) then fil_tev[ev]=nil end
- end
- end
- if not next(ofil_tev) then ofil[em]=nil end
- if not next(fil_tev) then fil[em]=nil end
- end
- end
- end
- ---Unregisters @obj from all the waiting sets it is linked to.
- function O:reset()
- local fil_tev
- local r=self.__root
- for em,tev in pairs(self.fil) do
- fil_tev=fil[em]
- if fil_tev then
- for ev in pairs(tev) do
- -- print(ev,'|',self,'|',fil_tev[ev],'|',fil_tev[ev][self])
- if fil_tev[ev] then
- fil_tev[ev][r]=nil
- if not next(fil_tev[ev]) then fil_tev[ev]=nil end
- end
- end
- if not next(fil_tev) then fil[em]=nil end
- end
- end
- self.fil={}
- end
- local ending={n=0}
- --helper
- ---Kills @obj 's subs and removes it from the waiting sets.
- function O:silent_destroy()
- local root=self.__root
- if ending[root] then
- error('Detected recursion in Obj.finalize: ending stack='..pstring(ending),2)
- else
- ending.n=ending.n+1
- ending[root]=ending.n
- end
- --handle subs
- while next(self.subs) do
- next(self.subs):destroy()
- end
- if self.parent then
- self.parent.Obj.subs[root]=nil
- end
- --remove self from the waiting sets
- self:reset()
- --remove self from the action queue
- sched.queue:remove(root)
- ending[root]=nil
- ending.n=ending.n-1
- end
- ---same as @O:silent_destroy, but throws some useful warning signals.
- function O:destroy(...)
- if not self.destroyed then
- local r=self.__root
- signal(r,'dying',...) --warns subs
- self:silent_destroy()
- signal(r,'dead',...)
- self.destroyed=true
- end
- end
- local function check_tree(self,new_parent,seen)
- if new_parent==nil then return true,seen end
- new_parent=new_parent.__root
- local r=self.__root
- seen=seen or {r,[r]=true}
- if seen[new_parent] then return false,seen end
- table.insert(seen,new_parent)
- seen[new_parent]=true
- return check_tree(new_parent,new_parent.Obj.parent,seen)
- end
- ---Changes an @obj parent field.
- function O:setParent(parent)
- local rparent,oparent=parent.__root,parent.Obj
- local root=self.__root
- local ok, t=check_tree(self,self.parent)
- if not ok then error('Cycle detected in scheduler hierarchy;debug:'..pstring(t),2) end
- if rparent==root then error("A scheduler object cannot be it's own parent",2) end
- if self.parent then self.parent.Obj.subs[root]=nil end
- self.parent=rparent
- oparent.subs[root]=true
- log('sched','DETAIL','(%s) parent set to (%s) by (%s)',self.__root,(parent and parent.__root),sched.running or 'nil')
- end
- local TIMEOUT_TOKEN={}
- ---helpers for linking @obj to timer signals
- function O:setTimeout(timeout)
- if type(timeout)~='number' then
- error('timeout must be number',2)
- else
- timeout=timer.norm(timeout)
- end
- self:cancelTimeout()
- self[TIMEOUT_TOKEN]={
- timeout=timeout,
- td={timer={timeout+os_time()}},
- }
- self:link(self[TIMEOUT_TOKEN].td)
- return self
- end
- ---cancels the current timeout and sets a new one in @timeout seconds, where @timeout was set by @O:setTimeout
- function O:resetTimeout()
- local td=self[TIMEOUT_TOKEN]
- if td then
- self:unlink(td.td)
- td.td[timer][1]=os_time()+td.timeout
- self:link(self.td.td)
- end
- end
- ---cancels the current timeout
- function O:cancelTimeout()
- local td=self[TIMEOUT_TOKEN]
- if td then
- self:unlink(td.td)
- self.td=nil
- end
- end
- function O:__debug()
- local fil={}
- for i,v in pairs(self.fil) do
- local k=debug.getinfo(i)
- fil[k]={}
- for j in pairs(v) do
- table.insert(fil[k],debug.getinfo(j))
- end
- end
- return string.format('%s; \nWait descriptor:%s',debug.objinfo(self),pstring(fil))
- end
- end
- --[[syncronous calls; are called as soon as the signal is received, but can't block as tasks do
- once are one-use,
- on are permanent,
- ]]
- Sync=class('Sync',Obj)
- do
- local S=Sync
- local sync_once_handle = function(self,...)
- local sync=self.Sync
- local running=sched.running
- sched.running=self
- sync.f(...)
- self:destroy()
- sched.running = running
- end
- local sync_on_handle=function(self,...)
- local running=sched.running
- sched.running=self
- local sync=self.Sync
- sync.f(...)
- self:resetTimeout()
- sched.running=running
- end
- function S:__init(f,mode)
- check('function',f)
- mode=mode or 'on'
- Obj:__init(self)
- if mode=='once' then
- elseif mode=='on' then
- else error('mode?',2) end
- self.f=f
- self.mode=mode
- log('sched','INFO','(%s) created with mode %s by (%s)',self,mode,sched.running or 'nil')
- end
- function S:handle(...)
- if self.mode=='on' then
- sync_on_handle(self,...)
- else
- sync_once_handle(self,...)
- end
- end
- end
- --asyncronous calls through coroutines
- Task=class('Task',Obj)
- do
- local T=Task
- ---Creates a task object in paused mode;
- ---fields set by Task.new are
- --co: matching coroutine
- --args: table of args to unpack and pass to co
- --parent: parent task
- --subs: sub tasks (see @{Task.setSub} and @{Task.setParent}
- --name: name used for logging and result of tostring(task)
- --Can take either (f) or (name,f) as args
- --@return task
- function T:__init(f,...)
- check('function',f)
- Obj:__init(self)
- self.co=coroutine.create( f )
- self._resume=function(co)
- s,m=coroutine.resume(self.co,unpack(self.args))
- self._resume=coroutine.resume
- return s,m
- end
- self.args={...}
- log('sched', 'INFO', 'created (%s) from function (%s) by (%s)', self, f,sched.running or 'nil')
- end
- ---Schedules a task for execution with args (...)
- --If T:handle is called several times before resuming the task, task.args will be overwritten and the last args set will be the last ones.
- function T:handle(...)
- self.args={...} --no point in catching args after nil, since self.args is unpacked before returning to the task.
- sched.queue:push(self)
- sched.ready=true
- log('sched', 'DETAIL', 'Task:handle rescheduling (%s) to receive SIGNAL [%s].[%s]',
- self,...)
- return self
- end
- ---Runs a task with initial args ...
- --@param vararg optional arguments to start the task with
- --@return task
- function T:run(...)
- self.args={...}
- sched.queue:push(self)
- log('sched', 'INFO', "Task.run scheduling (%s)", self)
- return self
- end
- --- Finishes a task and destroys it's subs.
- -- @param self task to terminate (see @{Task.new})
- -- @return task
- function T:destroy()
- if self.status~='dead' then
- log('sched', 'INFO', "Task.destroy destroying (%s) from (%s)",self,sched.running or 'nil')
- self.Obj:destroy()
- self.status='dead'
- if Task.running and Task.running.status=='dead' and Task.running.co==coroutine.running() then
- coroutine.yield()
- end
- end
- return self
- end
- function T:resume()
- sched.running = self
- local co=self.co
- Task.running=self
- log('sched', 'DETAIL', "Resuming (%s)", self)
- local success, msg = self._resume(co)
- log('sched', 'DETAIL', "Exited (%s)", self)
- Task.running=nil
- self.args={}
- sched.running = nil
- if not success then
- -- report the error msg
- log('sched', 'ERROR', "In (%s):%s",self,msg)
- signal(self, "error",success, msg)
- --preserve events/subs for error catchers to analize, and then finalize
- self:destroy(success,msg)
- elseif coroutine.status (co) == "dead" then --If the coroutine died, signal it for those who synchronize on its termination.
- log('sched', 'INFO', "(%s) is dead", self)
- self:destroy(success,msg)
- end
- end
- --- Waits for a signal
- --Can only be called from a task
- --If called with no arguments, yields and reschedules for execution
- --If called with false as argument, yields without rescheduling for execution
- -- @param vararg an entries descriptor for the signal (see @{get_args})
- -- @return emitter, event, parameters
- function T:wait(...)
- local task = self
- if task.co~=coroutine.running() then error('calling Task.wait outside a task/inside a task but inside another coroutine',2) end
- if (...)==nil then
- sched.queue:push(task)
- log('sched', 'DETAIL', "Task.wait rescheduling (%s) for resuming ASAP", task)
- elseif ... then
- task:link(...)
- log('sched', 'DETAIL', "(%s) waiting", task)
- else
- log('sched', 'DETAIL', "(%s) waiting for pre-set signals", task)
- end
- coroutine.yield ()
- if ... then
- task:reset()
- end
- return unpack(task.args)
- end
- ---resets the Task class vars
- function Task._reset()
- Task.running=nil
- end
- ---Some pre-built utilities for tasks
- --- Create a task that listens for a signal.
- -- @param f function to be called when the signal appears. The signal
- -- is passed to f as parameter. The signal will be provided as
- -- emitter, event, parameters, just as the result of a @{wait}
- -- @param vararg a Wait Descriptor for the signal (see @{get_args})
- -- @return task in the scheduler
- Task.sigrun= function (f,...)
- local ff=function()
- while true do
- f(sched.wait(false))
- end
- end
- local t=Task(ff)
- t:link(...)
- return t
- end
- --- Create a task that listens for a signal, once.
- -- @param f function to be called when the signal appears. The signal
- -- is passed to f as parameter. The signal will be provided as
- -- _emitter, event, parameters_, just as the result of a @{wait}
- -- @param vararg a Wait Descriptor for the signal (see @{get_args})
- -- @return task in the scheduler
- Task.sigrunonce= function (f,...)
- local ff=function()
- f(sched.wait(false))
- end
- local t=Task(ff)
- t:link(...)
- return t
- end
- end
- do
- sched={
- --modules and respective shortcuts
- signal=signal,
- emit=function(...)
- sched.signal(sched.me(),...)
- end,
- Obj=Obj,
- ready=false,
- me=function() return sched.running end,
- Task=Task,
- Sync=Sync,
- -- Call=Call,
- -- Wait=Wait,
- sigonce=function(f) return Sync(f,'once') end,
- sighook=function(f) return Sync(f,'on') end,
- wait=function(...) return sched.running:wait(...) end,
- sigrun=Task.sigrun,
- sigrunonce=Task.sigrunonce,
- --others
- }
- local _require=function(p,t)
- t=t or{}
- t.env={sched=sched}
- return require(p,t)
- end
- platform=_require'platform'
- os_time=platform.time
- timer=_require'timer'
- _require'pipe'
- _require'catalog'
- local loop_state = 'stopped' -- stopped, running or stopping
- function sched.step()
- local old = sched.queue
- sched.queue=Linked()
- --------------------------------------------------------------------
- -- going through `self.queue` until it's empty.
- --------------------------------------------------------------------
- while true do
- local obj = old:remove() --pops first from left->right
- if obj==nil then break end
- obj:resume()
- end
- if not sched.queue:isempty() then sched.ready=true end
- end
- ---Exits the scheduler after the current cycle is completed.
- function sched.stop()
- log('sched','INFO','(%s) toggling loop_state=%s to stopping',Task.running or 'scheduler',loop_state)
- if loop_state=='running' then loop_state = 'stopping' end
- end
- ---resets internal vars
- function sched.reset()
- fil={}
- sched.fil=fil
- sched.running=nil
- sched.queue=Linked()
- Task._reset()
- timer._reset()
- platform._reset() --after timer
- loop_state = 'stopped'
- log('sched','INFO','scheduler cleaned.')
- end
- ---Loops over the scheduler cycle,
- --returning to the caller function after sched.stop has been called
- --calling platform.step for performing sleeps.
- function sched.loop ()
- log('sched','INFO','Scheduler started')
- loop_state = 'running'
- local timeout
- while true do --this block is the scheduler step
- timer.step() -- Emit timer signals
- sched.step() -- Run all the ready tasks
- -- Find out when the next timer event is due
- timeout = nil
- local date = timer.nextevent()
- if date then
- local now=os_time()
- timeout = date<now and 0 or date-now
- end
- if loop_state~='running' then sched.reset() break end
- --debug.log(sched.debug())
- platform.step(timeout) -- Wait for platform events until the next timer is due
- sched.ready=false
- end
- end
- function sched.debug()
- local d={}
- local t={}
- for em,emt in pairs(fil) do
- local tt={}
- for ev,objt in pairs(emt) do
- local ttt={}
- for obj in pairs(objt) do
- table.insert(ttt,debug.getinfo(obj))
- end
- tt[debug.getinfo(ev)]=ttt
- end
- t[debug.getinfo(em)]=tt
- end
- d.fil=t
- return pstring(d)
- end
- local t={}
- for i,v in pairs(sched) do
- if type(i)=='string' then t[i:lower()]=v end
- end
- for i,v in pairs(t) do sched[i]=v end
- sched.reset()
- end
- return sched]===================],"CoolisOS/bin/kernel/sched/lock.lua",[===================[--------------------------------------------------------------------------------
- -- Thread-bound lock objects
- -- =========================
- --
- -- A lock allows to define atomic units of code execution:
- -- a portion of code that is protected by a lock will never be executed by more
- -- than one thread at a time.
- -- Because of the use of coroutines for the threading in Lua, the use of locks
- -- is only relevant when you want to protect a portion of code that contains
- -- blocking APIs (i.e. network access, scheduling, etc...)
- --
- -- A lock must be acquired and released by the same thread: an error will be
- -- triggered if a thread tries to release a lock acquired by another thread.
- -- This behavior is enforced because it is almost always a concurrency mistake
- -- to have a thread releasing another thread's locks; but you can circumvent
- -- this by passing the owning thread as an extra parameter.
- --
- -- A lock is automatically released when its owning thread dies, if it failed
- -- to do so explicitly.
- --
- -- API
- -- ---
- --
- -- lock.new()
- -- return a new instance of a lock.
- --
- -- LOCK :acquire ()
- -- acquire the lock.
- --
- -- LOCK :release ([thread])
- -- release the lock. The optional thread param is defaulted to the
- -- current thread. A lock must be released by the thread that
- -- acquired it (or use the thread params to release another thread's lock).
- --
- -- LOCK :destroy()
- -- destroy the lock. Any waiting thread on that lock will trigger a
- -- "destroyed" error
- --
- --------------------------------------------------------------------------------
- --------------------------------------------------------------------------------
- -- Associative lock objects
- -- ------------------------
- --
- -- Function lock (object) allows to associate a lock to an arbitrary object.
- -- This API creates a standard lock (as above). It removes the burden of
- -- creating the lock and managing the association between the object
- -- and the lock.
- --
- -- Locks associated to objects still have an owning thread, and the rule
- -- "locks must be released by the thread which owns them" still applies.
- --
- -- API
- --
- -- lock.lock (object)
- -- lock the object.
- --
- -- lock.unlock(object, thread)
- -- unlock the object. thread is optional as in lock:acquire() function.
- --
- --------------------------------------------------------------------------------
- --------------------------------------------------------------------------------
- -- Synchronized functions
- -- ----------------------
- --
- -- A common pattern is to have a function that must not be called more than once
- -- at a given time: if the function is already running in another thread, one
- -- must wait for this call to finish before calling it again.
- --
- -- API
- --
- -- lock.synchronized (f)
- -- return a synchronized wrapper around f. A typical idiom is:
- --
- -- mysynchronizedfunction = lock.synchronized (function (...)
- -- [code]
- -- end)
- --------------------------------------------------------------------------------
- include'kernel.class'
- local check = require 'kernel.checker'.check
- local sched = require 'init'
- local setmetatable = setmetatable
- local tostring = tostring
- local string = string
- local error = error
- local assert = assert
- local table = table
- local next = next
- local pairs = pairs
- local type = type
- local unpack = unpack
- env=getfenv()
- setmetatable(env,nil)
- --------------------------------------------------------------------------------
- --
- --------------------------------------------------------------------------------
- class.LOCK()
- LOCK.hooks={}
- LOCK.objlocks=setmetatable({}, {__mode = "k"})
- --------------------------------------------------------------------------------
- -- Create a new lock object. It remains unlocked.
- --------------------------------------------------------------------------------
- setmetatable(env,{__call=function() return LOCK(...) end})
- function LOCK:__init()
- self.waiting={}
- end
- --------------------------------------------------------------------------------
- -- Destroy a lock object.
- --------------------------------------------------------------------------------
- function LOCK:destroy()
- self.owner = "destroyed" -- means that the lock is being destroyed !
- for t, _ in pairs(self.waiting) do
- sched.signal(self, t)
- self.waiting[t] = nil
- end
- end
- --------------------------------------------------------------------------------
- -- Helper to release locks on dead threads.
- --------------------------------------------------------------------------------
- local function autorelease(thread)
- for l, _ in pairs(LOCK.hooks[thread]) do
- if l.owner == thread then l:release(thread)
- elseif l.waiting then l.waiting[thread] = nil end
- end
- LOCK.hooks[thread] = nil
- end
- --------------------------------------------------------------------------------
- -- Helper to release locks on dead threads.
- --------------------------------------------------------------------------------
- local function protectdie(self, thread)
- if not LOCK.hooks[thread] then
- local h = sched.sigonce(function()
- autorelease(thread)
- end,thread, "dead")
- LOCK.hooks[thread] = {sighook = h}
- end
- (LOCK.hooks[thread])[self] = true
- end
- --------------------------------------------------------------------------------
- -- Helper to release locks on dead threads.
- --------------------------------------------------------------------------------
- local function unprotectdie(self, thread)
- (LOCK.hooks[thread])[self] = nil
- if not next(LOCK.hooks[thread], next(LOCK.hooks[thread])) then -- if this was the last lock attached to that thread...
- (LOCK.hooks[thread].sighook):destroy()
- LOCK.hooks[thread] = nil
- end
- end
- --------------------------------------------------------------------------------
- -- Attempt to take ownership of a lock; might block until the current owner
- -- releases it.
- --------------------------------------------------------------------------------
- function LOCK:acquire()
- local t = sched.task.running
- assert(self.owner ~= t, "a lock cannot be acquired twice by the same thread")
- assert(self.owner ~= "destroyed", "cannot acquire a destroyed lock")
- protectdie(self, t) -- ensure that the lock will be unlocked if the thread dies before unlocking...
- while self.owner do
- self.waiting[t] = true
- sched.wait(self,t) -- wait on the current lock with the current thread
- if self.owner == "destroyed" then error("lock destroyed while waiting") end
- end
- self.waiting[t] = nil
- self.owner = t
- end
- --------------------------------------------------------------------------------
- -- Release ownership of a lock.
- --------------------------------------------------------------------------------
- function LOCK:release(thread)
- thread = thread or sched.task.running
- assert(self.owner ~= "destroyed", "cannot release a destroyed lock")
- assert(self.owner == thread, "unlock must be done by the thread that locked")
- unprotectdie(self, thread)
- self.owner = nil
- -- wakeup a waiting thread, if any...
- local t = next(self.waiting)
- if t then
- sched.signal(self, t)
- end
- end
- --------------------------------------------------------------------------------
- -- Create and acquire a new lock, associated to an arbitrary object.
- --------------------------------------------------------------------------------
- function lock(object)
- assert(object, "you must provide an object to lock on")
- assert(type(object) ~= "string" and type(object) ~= "number", "the object to lock on must be a collectable object (no string or number)")
- if not LOCK.objlocks[object] then LOCK.objlocks[object] = new() end
- LOCK.objlocks[object]:acquire()
- end
- --------------------------------------------------------------------------------
- -- Release an object created with lock().
- --------------------------------------------------------------------------------
- function unlock(object, thread)
- assert(object, "you must provide an object to unlock on")
- assert(LOCK.objlocks[object], "this object was not locked")
- LOCK.objlocks[object]:release()
- end
- --------------------------------------------------------------------------------
- -- Create a synchronized version of function f, i.e. a function that behaves
- -- as f, except that no more than one instance of it will be running at a
- -- given time.
- --------------------------------------------------------------------------------
- function synchronized(f)
- check(f,'function')
- local function sync_f(...)
- local k = lock (f)
- local r = {f(...)}
- unlock(f)
- return unpack (r, 1, r.n)
- end
- return sync_f
- end
- return env
- ]===================],"CoolisOS/bin/kernel/sched/pipe.lua",[===================[--------------------------------------------------------------------------------
- -- FIFO PIPES
- -- ==========
- --
- -- Purpose
- -- -------
- -- Tasks can't easily exchange messages with sighook() or wait():
- --
- -- Calling wait() in a loop might lose some messages, if a message arrives
- -- before the loop had the time to go back to wait(). Example:
- --
- -- ----------------------------------------------------------
- -- -- sender --
- -- for i=1, 100 do signal('foo', 'tick', i); sched.wait (2) end
- --
- -- -- receiver --
- -- while true do
- -- local _, n = sched.wait('foo', 'tick')
- -- printf ("received %i", n)
- -- sched.wait(3)
- -- end
- -- ----------------------------------------------------------
- --
- -- sighooks are guaranteed to be called everytime the event is triggered,
- -- but they are executed as interruptions, they can't pause a task.
- --
- -- Pipes solve this issue: everything that is written in a pipe with :send()
- -- will be read back, in order, with :receive(). If there's nothing to read yet,
- -- the task calling :receive() will pause and wait for the next :send() to
- -- provide data to read back.
- --
- -- :receive() can yield, and therefore must not be used in a sighook.
- --
- -- :send() can yield only if there is a maxlength and the pipe is already full.
- --
- --
- -- API
- -- ---
- -- - create a new pipe with:
- -- P = pipe()
- -- - push data in it with:
- -- P :send(x). x can't be nil.
- -- - read data back with:
- -- P :receive() or P :receive(timeout).
- -- This call will block if necessary. It also supports an optional timeout
- -- parameter. If there is a timeout and nothing can be read within the delay,
- -- nil is returned.
- -- - do a non-blocking, non-consumming peek at next value with:
- -- P :peek()
- -- This might return nil is the pipe is currently empty.
- -- - return all of the pipe's current content and reset it as empty with:
- -- P :reset()
- -- - cancel a reading by pushing back an element in the pipe with:
- -- P :pushback(x)
- -- - retrieve the number of elements currently in a pipe with:
- -- P :length()
- -- - tune the recompaction parameters:
- -- P :setwaste(absolute_waste_factor, proportional_waste_factor)
- --
- --
- -- Algorithmic complexity
- -- ----------------------
- -- The amortized complexity for sending and receiving is o(log(N)), N
- -- being the current number of elements in the pipe. Notice that with
- -- the obvious/naive implentation based on table.insert() and
- -- table.remove(), one of the operations is in o(log(N)) but the other
- -- is in o(N); therefore, writing K elements then reading them back
- -- would have a time complexity in o(K^2), compared to o(K*log(K)) here.
- --
- --------------------------------------------------------------------------------
- include'kernel.class'
- local check=require 'kernel.checker'.check
- local sched=sched
- --------------------------------------------------------------------------------
- -- Object metatable/class
- --------------------------------------------------------------------------------
- sched.Pipe=class'Pipe'
- do
- local P=sched.Pipe
- local insert, remove, os_time = table.insert, table.remove, os.clock
- --------------------------------------------------------------------------------
- -- A registry of all currently active pipes. This is intended as a debug aid,
- -- and can be safely commented out if not needed
- --------------------------------------------------------------------------------
- sched.pipes = setmetatable({}, {__mode='kv'})
- --------------------------------------------------------------------------------
- -- Create a new pipe
- --------------------------------------------------------------------------------
- function P:__init(maxlength)
- check ('?number',maxlength)
- util.shcopy({
- sndidx = 1;
- rcvidx = 1;
- content = { };
- maxlength = maxlength;
- state = 'empty';
- wasteabs = 32 ;
- wasteprop = 2 },
- self)
- if sched.pipes then
- sched.pipes[debug.getinfo(self)]=self
- end
- end
- --------------------------------------------------------------------------------
- -- Check whether the 'state' field is consistent with the pipe's content
- -- and maxlength, produce a signal and change it if needed.
- -- Helper for methods which modify content or maxlength.
- --------------------------------------------------------------------------------
- local function updatestate (self)
- local length = self.sndidx - self.rcvidx
- local oldstate = self.state
- local newstate = length==0 and 'empty' or self.maxlength==length and 'full' or 'ready'
- if oldstate ~= newstate then
- self.state=newstate
- sched.signal(self, 'state', newstate)
- end
- end
- --------------------------------------------------------------------------------
- -- Move all entries to the beginning of content, if the following conditions
- -- are both verified:
- -- * there are more than self.wasteabs cells to collect;
- -- * the number of wasted cells is at more than self.wasteprop times bigger
- -- than the number of actually used cells.
- --------------------------------------------------------------------------------
- local function recompact (self)
- local rcvidx = self.rcvidx
- local wasted = rcvidx-1
- if wasted <= self.wasteabs then return end
- local sndidx = self.sndidx
- local used = sndidx - rcvidx
- if wasted <= self.wasteprop * used then return end
- -- Tests passed, proceed to recompaction
- local content
- if sndidx<2000 then content = { select (rcvidx, unpack (self.content)) } else
- -- avoid stack overflows: stacks can't grow beyond LUAI_MAXCSTACK,
- -- which is set to 8000 by default. Our choice of 2000 is just as
- -- arbitrary as 8000.
- local oldcontent
- oldcontent, content = self.content, { }
- for i=1,used do content[i]=oldcontent[i+wasted] end
- end
- self.content, self.rcvidx, self.sndidx = content, 1, used+1
- end
- --------------------------------------------------------------------------------
- -- Read a value, yield the current task if necessary
- --------------------------------------------------------------------------------
- function P :receive (timeout)
- check('?number',timeout)
- local due_date
- while true do
- if self.rcvidx==self.sndidx then
- log('pipe', 'DEBUG', "(%s) empty,(%s) :receive() waits for data", self,sched.running)
- due_date = due_date or timeout and os_time() + timeout
- local timeout = due_date and due_date - os_time()
- if timeout and timeout<=0 or sched.wait(self,'state',timeout)==sched.timer then
- return nil, 'timeout'
- end
- else
- local content, rcvidx = self.content, self.rcvidx
- local x = content[rcvidx]
- content[rcvidx] = false
- self.rcvidx = rcvidx+1
- recompact (self)
- updatestate (self)
- if x~=nil then return x end
- end
- end
- end
- --------------------------------------------------------------------------------
- -- Push a value to read
- --------------------------------------------------------------------------------
- function P :send (x, timeout)
- check ('!nil,?number',x,timeout)
- local maxlength = self.maxlength
- local due_date
- while self.state == 'full' do
- log('pipe', 'DEBUG', "Pipe %s full, :send() blocks until some data is pulled from pipe", self)
- due_date = due_date or timeout and os_time() + timeout
- local timeout = due_date and due_date - os_time()
- if timeout and timeout<=0 or sched.wait(self,'state', timeout)=='timer' then
- log('pipe', 'DEBUG', "Pipe %s :send() timeout", self)
- return nil, 'timeout'
- else
- log('pipe', 'DEBUG', "Pipe %s state changed, retrying to :send()", self)
- end
- end
- local sndidx = self.sndidx
- self.content[sndidx] = x
- self.sndidx = sndidx+1
- updatestate(self)
- return self
- end
- --------------------------------------------------------------------------------
- -- Pushback the last read value
- --------------------------------------------------------------------------------
- function P :pushback (x)
- assert (x~=nil, "Don't :pushback(nil) in a pipe")
- if self.state=='full' then
- return nil, 'length would exceed maxlength'
- end
- local rcvidx = self.rcvidx-1
- if rcvidx==0 then
- table.insert(self.content, 1, x)
- self.sndidx = self.sndidx+1
- else
- self.content[rcvidx] = x
- self.rcvidx = rcvidx
- end
- updatestate (self)
- return self
- end
- --------------------------------------------------------------------------------
- -- Empty the pipe, return what it contained in a list
- --------------------------------------------------------------------------------
- function P :reset()
- local r = self.content
- self.content = { }
- updatestate(self)
- return r
- end
- --------------------------------------------------------------------------------
- -- Try to read without yielding nor removing from internal queue
- --------------------------------------------------------------------------------
- function P :peek()
- return self.content[self.rcvidx]
- end
- --------------------------------------------------------------------------------
- -- Return the number of elements currently in the pipe.
- --------------------------------------------------------------------------------
- function P :length()
- return self.sndidx - self.rcvidx
- end
- --------------------------------------------------------------------------------
- -- Change or remove (by passing nil) the curent pipe length limitation.
- --------------------------------------------------------------------------------
- function P :setmaxlength(maxlength)
- check ('?number',maxlength)
- if self :length() > maxlength then return nil, 'length exceeds new maxlength' end
- if maxlength and maxlength<1 then return nil, 'invalid maxlength' end
- self.maxlength = maxlength
- updatestate (self)
- return self
- end
- --------------------------------------------------------------------------------
- -- Change waste parameters, hence the memory/speed performance compromize.
- --------------------------------------------------------------------------------
- function P :setwaste(abs, prop)
- self.wasteabs, self.wasteprop = abs, prop
- return self
- end
- end
- sched.SigPipe=class('SigPipe',sched.Pipe,sched.Obj)
- do
- local S=sched.SigPipe
- function S:handle(...)
- self:send{...}
- end
- end
- sched.pump=function(pin,f,pout)
- check('?Pipe,?function,?Pipe',pin,f,pout)
- if not (pin or f or pout) then error'No pipes or function' end
- repeat
- local r=pin and pin:receive()
- r=f==nil and r or f(r)
- if r~=nil and pout then pout:send(r) end
- until false
- end
- sched.to=function(pin,pouts)
- repeat
- local x=pin:receive()
- if x~=nil then
- for i,pout in ipairs(pouts) do
- pout:send(x)
- end
- end
- until false
- end
- sched.from=function(pins,pout)
- sched.running:reset()
- for _,pin in ipairs(pins) do
- sched.running:link(pin,'*')
- end
- repeat
- local redo=true
- while redo do
- redo=false
- for _,pin in ipairs(pins) do
- if pin:peek()~=nil then
- pout:send(pin:receive())
- redo=true
- end
- end
- end
- sched.wait(false)
- until false
- end
- ]===================],"CoolisOS/bin/kernel/sched/platform.lua",[===================[local log=require'kernel.log'
- local sched,pairs,string,term,_G,fs,os,print,unpack,select,pprint,next=sched,pairs,string,term,_G,fs,os,print,unpack,select,pprint,next
- local coroutine=coroutine
- local tostring,math=tostring,math
- local debug,util=debug,util
- local plat={}
- local sched=sched
- local fil
- env=getfenv()
- setmetatable(env,nil)
- ---Whether or not the coroutine running will yield to the scheduler
- plat.is_running=function()
- return sched and sched.Task.running and sched.Task.running.co==coroutine.running()
- end
- plat.time = os.clock
- local time=plat.time
- local wake_ev=tostring({})
- local Task=sched.Task
- local cc,CC,load=0,1,0
- last_yield,last_return=-math.huge,-math.huge
- plat.info=function()
- return string.format('load: %f;last yield: %f;last return:%f',util.format_number(load,2),util.format_number(last_yield,2),util.format_number(last_return,2))
- end
- local yield=function(...)
- log('platform','DETAIL','yielding to platform')
- last_yield=time()
- cc=last_yield-last_return
- local x={coroutine.yield(...)}
- last_return=time()
- CC=last_return-last_yield
- load=cc/(CC+cc)
- log('platform','DETAIL',plat.info())
- return x
- end
- function plat.step(timeout)
- if sched.ready then timeout=0 end
- if timeout then
- local ev,id
- if timeout==0 then
- if time()-last_return>=0.05 then --retain control for at most (as far as the scheduler can control) one tick if necessary; set to math.huge in case there is too much of a delay between computers;
- ev=wake_ev
- os.queueEvent(ev)
- else
- return
- end
- else
- ev,id='timer',os.startTimer(timeout)
- end
- local x
- repeat
- x=yield()
- if (ev==nil or x[1]==ev) and (id==nil or id==x[2]) then
- break
- else
- sched.signal(plat,unpack(x))
- if sched.ready then break end
- end
- until false
- return
- else
- if not fil[plat] then
- log('platform','ERROR','no timers or platform listeners, so exiting.')
- sched.stop()
- return
- end
- local filter
- if not next(fil[plat],next(fil[plat])) then
- filter=next(fil[plat])
- end
- sched.signal(plat,unpack(yield(filter)))
- return
- end
- end
- function plat._reset()
- fil=sched.fil
- end
- debug.name(plat,'platform')
- sched.platform=plat
- return plat]===================],"CoolisOS/bin/kernel/sched/timer.lua",[===================[------------------------------------------------------------------------------
- -- timer module
- -- supports one time timer timer.new(positive number)
- -- supports periodic timer timer.new(negative number)
- -- cron-compatible syntax (string conforming to cron syntax)
- -- support simple timers
- ------------------------------------------------------------------------------
- include'kernel.class'
- local linked=require'utils.linked'
- local check=require'kernel.checker'.check
- local shcopy=require'kernel.util'.shcopy
- local error=error
- local read=read
- PACKAGE_NAME='sched'
- local sched=sched
- local fil
- local Obj=sched.Obj
- local os = os
- local math = math
- local tonumber = tonumber
- local assert = assert
- local table = table
- local pairs = pairs
- local next = next
- local type = type
- local _G=_G
- local os_time=os.clock
- local setmetatable=setmetatable
- local rawset=rawset
- local unpack=unpack
- local debug=debug
- env=getfenv()
- setmetatable(env,nil)
- local timer={}
- timer.norm=function(t) --the CC clock moves in steps of 0.05
- t=t-t%0.05
- -- if t<=0 then
- -- error('time values must be non-negative and multiples of 0.05',3)--to be used internally inside timer functions
- -- end
- return t
- end
- local norm=timer.norm
- -------------------------------------------------------------------------------------
- -- Common scheduling code
- -------------------------------------------------------------------------------------
- -------------------------------------------------------------------------------------
- -- These functions must be attached as method :nextevent() to timer objects,
- -- and return the timer's next due date.
- -------------------------------------------------------------------------------------
- local function stimer_nextevent (timer) return nil end
- local events
- local link,link_r
- local n
- -------------------------------------------------------------------------------------
- -- Take a timer, reference it
- -------------------------------------------------------------------------------------
- timer.add = function(t)
- if not link_r[t] then
- local ind
- for val in linked.next_r,link,val do
- if val>t then
- ind=val
- break
- end
- end
- -- print(ind)
- if ind==nil then sched.ready=true end--if the next due date changed, signal the platform to break it's step
- link:insert_r(t,ind)
- end
- end
- -------------------------------------------------------------------------------------
- -- Take a timer, dereference it
- -------------------------------------------------------------------------------------
- timer.remove = function(t)
- link:remove(t)
- end
- -------------------------------------------------------------------------------------
- -- Signal all elapsed timer events.
- -- This must be called by the scheduler every time a due date elapses.
- -------------------------------------------------------------------------------------
- function timer.step()
- if link_r[0]==-1 then return end -- if no timer is set just return and prevent further processing
- local now = os_time()
- while link_r[0]~=-1 and now >= link_r[0] do --about the above redundancy, maybe while loops are heavy?
- local nd = link:remove()
- sched.signal(timer,nd)
- end
- end
- timer.meta={__index=timer}
- setmetatable(timer,{__index=Obj,__tostring=function() return 'Class timer' end})
- -------------------------------------------------------------------------------------
- -- returns the next expiration date
- -------------------------------------------------------------------------------------
- function timer.nextevent()
- -- print(link)
- if link_r[0]~=-1 then return link_r[0] end
- end
- local meta={
- __newindex=function(t,k,v)
- if type(k)~='number' then
- error(tostring(k)..'timer events must be numbers',2)
- end
- -- k=k-k%0.05 --math.ceil(t/0.05)*0.05 --maybe round up for user comfort? this way it's 1.5 time faster
- -- if k<=0 then
- -- error(k..'time values must be positive',2)
- -- end
- if v==nil then
- timer.remove(k)
- else--could move it here...
- timer.add(k)
- end
- rawset(t,k,v)
- end,
- }
- ---resets the timer module
- timer._reset=function()
- local holder=sched.task(function() end)
- debug.name(holder,'placeholder')
- events=setmetatable({[{}]={[holder]=true}},meta)
- sched.fil[timer]=events
- link=linked()
- link_r=link.r
- timer.link=link
- end
- class.CyclicTimer(sched.Obj)
- do
- local C=CyclicTimer
- local O=sched.Obj
- function C:__init(delta,...)
- O:__init(self)
- check('number,function',delta,f)
- self.delta=norm(delta)
- self.f=f
- self.args={...}
- end
- function C:handle(_,ev)
- ev=ev+self.delta
- self:link{timer={ev}}
- self.f(unpack(self.args))
- end
- end
- timer.cyclic=CyclicTimer
- debug.name(timer,'timer')
- sched.timer=timer
- return timer]===================],"CoolisOS/bin/programs",true,"CoolisOS/bin/programs/bt.lua",[===================[print(1)]===================],"CoolisOS/bin/programs/help.lua",[===================[--By CoolisTheName007
- --CoolisOS/dev/help.lua
- local mon=peripheral.wrap'back'
- mon.setTextScale(1)
- local delay=1
- local textPath='CoolisOS/dev/text'
- local function getText()
- local f=fs.open(textPath,'r')
- local text=f.readAll()
- f.close()
- return text
- end
- local function parse_text(text)
- for line in text:gmatch("([^\n]*)\n?") do
- local t,b=line:match'^#([^#- ]+)-?([^#- ]*)#'
- end
- end
- local function write( sText)
- local term=mon
- local w,h = term.getSize()
- local x,y = term.getCursorPos()
- local nLinesPrinted = 0
- local function newLine()
- if y + 1 <= h then
- term.setCursorPos(1, y + 1)
- else
- term.setCursorPos(1, h)
- term.scroll(1)
- end
- x, y = term.getCursorPos()
- nLinesPrinted = nLinesPrinted + 1
- end
- -- Print the line with proper word wrapping
- while string.len(sText) > 0 do
- local whitespace = string.match( sText, "^[ \t]+" )
- if whitespace then
- -- Print whitespace
- term.write( whitespace )
- x,y = term.getCursorPos()
- sText = string.sub( sText, string.len(whitespace) + 1 )
- end
- local newline = string.match( sText, "^\n" )
- if newline then
- -- Print newlines
- newLine()
- sText = string.sub( sText, 2 )
- end
- local text = string.match( sText, "^[^ \t\n]+" )
- if text then
- sText = string.sub( sText, string.len(text) + 1 )
- if string.len(text) > w then
- -- Print a multiline word
- while string.len( text ) > 0 do
- if x > w then
- newLine()
- end
- term.write( text )
- text = string.sub( text, (w-x) + 2 )
- x,y = term.getCursorPos()
- end
- else
- -- Print a word normally
- if x + string.len(text) - 1 > w then
- newLine()
- end
- term.write( text )
- x,y = term.getCursorPos()
- end
- end
- end
- return nLinesPrinted
- end
- local function wrap(text, limit) --from Kingdaroo
- local lines = {''}
- for word, space in text:gmatch('(%S+)(%s*)') do
- local temp = lines[#lines] .. word .. space:gsub('\n','')
- if #temp > limit then
- table.insert(lines, '')
- end
- if space:find('\n') then
- lines[#lines] = lines[#lines] .. word
- space = space:gsub('\n', function()
- table.insert(lines, '')
- return ''
- end)
- else
- lines[#lines] = lines[#lines] .. word .. space
- end
- end
- return lines
- end
- local function extract(t,s,l)
- local c={}
- for k=s,s+l-1 do
- c[k-s+1]=t[k%(#t)+1]
- end
- return c
- end
- local function print_screen(t)
- mon.clear()
- mon.setCursorPos(1,1)
- local x,y=mon.getSize()
- for i,line in ipairs(t) do
- if (line:sub(1,1)=='#') then
- local tc=colors[line:match'^#([^-#]+)']
- mon.setTextColor(tc or colors.white)
- local bc=colors[line:match'^#[^-#]+%-([^#]+)#']
- mon.setBackgroundColor(bc or colors.black)
- local l=line:match'^#[^#]+#'
- line=line:sub(l:len()+1)
- end
- c=' '
- if (line:sub(-2,-2)=='&') then
- c=line:sub(-1,-1)
- line=line:sub(1,-3)
- end
- line=line..string.rep(c,math.max(x-#line,0))
- write(line..(i~=#t and '\n' or ''))
- end
- end
- local function main()
- local x,y=mon.getSize()
- local text
- local c=-1
- repeat
- local ok,r=pcall(getText)
- if ok then text=r else error(r) end
- local tText=wrap(text,x)
- c=(c+1)%(#tText)
- mon.setTextColor(colors.white)
- mon.setBackgroundColor(colors.black)
- local t=tText
- for i=1,(c+1) do
- local line=t[i]
- if (line:sub(1,1)=='#') then
- local tc=colors[line:match'^#([^-#]+)']
- mon.setTextColor(tc or colors.white)
- local bc=colors[line:match'^#[^-#]+%-([^#]+)#']
- mon.setBackgroundColor(bc or colors.black)
- end
- end
- print_screen(extract(tText,c,y))
- sleep(delay)
- until false
- end
- parallel.waitForAny(main,function() shell.run'shell' end)
- ]===================],"CoolisOS/bin/programs/master.lua",[===================[rednet.open'right'
- function slave()
- local t={
- 1=true
- }
- local send=function(id,n) turtle.select(id) turtle.drop(n) end
- while true do
- local _id,msg=rednet.receive()
- local id,n=msg:match'([^:]):([^:])'
- if id and n then
- n=tonumber(n)
- send(id,n)
- end
- end
- end
- function master()
- delay=5
- function _request(id,n)
- repeat
- rednet.broadcast(id..':'..n)
- t=0
- repeat
- if turtle.getItemCount(1)==n then break end
- sleep(dt)
- t=t+dt
- until t>delay
- until turtle.getItemCount(1)==n
- turtle.drop()
- end
- function request(id,n)
- local r=n%64
- local q=(n-r)/64
- for i=1,q do _request(id,64) end
- _request(id,r)
- end
- end]===================],"CoolisOS/bin/programs/quarry.lua",[===================[--CoolisOS/dev/quarry.lua
- include'kernel.class'
- class.Machine()
- local function link(t)
- if t[1] then t[1].start=true end
- for i,v in ipairs(t) do
- v.next=t[i+1]
- v.index=i
- end
- end
- function Machine:__init(t)
- link(t)
- self.states=t
- end
- function Machine:find()
- for i,v in ipairs(self.states) do
- if v.is() then
- return v
- end
- end
- error'no match for state'
- end
- function Machine:run(state)
- state=state or self.states[1]
- while state do
- state.action()
- state=state.next
- end
- end
- Vec3=require'utils.Vec3'
- local delay=5
- local max_count=300
- Var=require'kernel.class.Var'
- var=Var('ect/quarry.var')
- winding=require'programs.winding'
- include'utils.turtle'
- select=function(t)
- if type(t)~='number' then t=t.slot end
- return turtle.select(t)
- end
- drop=turtle.drop
- refuel=turtle.refuel
- function clear_inv()
- for i=7,16 do
- select(i)
- drop()
- end
- end
- function dirClear(dir) select(16) clear(dir) clear_inv() end
- api.ensureFuel=function()
- if turtle.getFuelLevel()<10 then
- refuel_machine:run()
- end
- end
- F,R,C,M,T,I={slot=1},{slot=2},{slot=3},{slot=4},{slot=5},{slot=6}
- function isInv(a,b,c,d)
- local function is(s)
- local c=turtle.getItemCount(s)
- if c==0 or c==1 then return c
- else error('inv not match') end
- end
- return is(2)==a and is(3)==b and is(4)==c and is(5)==d
- end
- function has(t)
- local c=turtle.getItemCount(t.slot)
- if c~=1 and c~=0 then error'no inv match'
- else return c==1 end
- end
- refuel_states={
- {
- is=function()
- return has(F)
- end,
- action=function()
- dirClear(up)
- select(F)
- place(up)
- end,
- },
- {
- is=function() return not has(F) end,
- action=function()
- select(16)
- drop()
- repeat
- up.suck()
- refuel()
- if turtle.getFuelLevel()>10 then
- break
- else
- drop()
- sleep(1)
- end
- until false
- select(F)
- up.dig()
- end,
- },
- }
- well_states={
- {is=function()
- return isInv(1,1,1,1)
- end,
- action=function()
- select(16)
- down.dig()
- drop()
- force'forward'
- drop()
- down.dig()
- drop()
- select(M.slot)
- place'down'
- select(16)
- local ok,n= force'back'
- if n then clear_inv() end
- select(C.slot)
- place'down'
- end,
- },
- {is=function()
- return isInv(1,0,0,1)
- end,
- action=function()
- select(R.slot)
- place(front)
- sleep(delay)
- end,
- },
- {is=function()
- return isInv(0,0,0,1) and front.detect()
- end,
- action=function()
- select(16)
- local ok,n= force'back'
- if n then clear_inv() end
- end},
- {is=function()
- return isInv(0,0,0,1) and not front.detect()
- end,
- action=function()
- select(T.slot)
- place(front)
- end,
- },
- {is=function()
- return isInv(0,0,0,0)
- end,
- action=function()
- local p=peripheral.wrap('front')
- p.shutdown()
- p.turnOn()
- select(R.slot)
- repeat
- front.suck()
- until turtle.getItemCount(R.slot)==1
- p.shutdown()
- end,
- },
- {is=function()
- return isInv(1,0,0,0)
- end,
- action=function()
- select(T.slot)
- front.dig()
- end,
- },
- {is=function()
- return isInv(1,0,0,1) and not down.detect()
- end,
- action=function()
- select(16)
- force'front'
- drop()
- end,
- },
- {is=function()
- return isInv(1,0,0,1)
- end,
- action=function()
- select(C.slot)
- down.dig()
- end,
- },
- {is=function()
- return isInv(1,1,0,1) and not down.detect()
- end,
- action=function()
- select(16)
- force'front'
- drop()
- end,
- },
- {is=function()
- return isInv(1,1,0,1) and down.detect()
- end,
- action=function()
- select(M.slot)
- down.dig()
- end,
- },
- }
- recharge_states={
- {
- is=function()
- return has(I)
- end,
- action=function()
- dirClear(up)
- select(I)
- place(up)
- end,
- },
- {
- is=function() return ((not has(I)) and has(R)) end,
- action=function()
- select(R)
- repeat
- up.drop()
- until (not has(R))
- end,
- },
- {
- is=function() return not (has(I) or has(R)) end,
- action=function()
- sleep(2)
- select(R)
- repeat
- up.suck()
- until (has(R))
- var.count=0
- select(I)
- up.dig()
- end,
- },
- }
- refuel_machine=Machine(refuel_states)
- recharge_machine=Machine(recharge_states)
- well_machine=Machine(well_states)
- wind_machine={
- run=function(self,center)
- function project(v)
- return {v.x,v.z}
- end
- local p=project((get())-center)
- var.count=var.count or 0
- repeat
- if var.count>max_count then
- recharge_machine:run()
- end
- -- print('actual:',p[1],p[2])
- local n=winding{p[1],p[2]}
- -- print('winding:',n[1],n[2])
- -- print('turning:',n[1]-p[1],0,n[2]-p[2])
- turn{n[1]-p[1],0,n[2]-p[2]}
- -- print('pos,dir:',get())
- local x,z=p[1],p[2]
- local d=math.max(math.abs(x),math.abs(z))
- if (not(x==d and z==(d-1))) and ((not(d==math.abs(x) and d==math.abs(z))) or (d==x and d==-z)) then
- well_machine:run()
- end
- local nex=center+Vec3{n[1],0,n[2]}
- -- print('to:',nex)
- goto(nex)
- p=n
- until false
- end,
- }
- function resume()
- order={
- refuel_machine,
- recharge_machine,
- well_machine,
- }
- for _,machine in ipairs(order) do
- local s=machine:find()
- if not s.start then
- machine:run(s)
- end
- end
- end
- function main(_delay,_max_count,reset)
- if reset then var.count=0; var.center=nil end
- delay=_delay or delay
- max_count=_max_count or max_count
- resume()
- var.center=var.center or (get())
- wind_machine:run(Vec3(var.center))
- end
- ]===================],"CoolisOS/bin/programs/question.lua",[===================[--CoolisOS/dev/question.lua
- local questions=loadfile'CoolisOS/dev/questions'()
- local set=rs.setOutput
- local setDoor=function(...) return set('back',...) end
- local setFire=function(...) return set('bottom',...) end
- for i,v in pairs(rs.getSides()) do
- set(v,false)
- end
- local right=function()
- reset_term()
- print'Right Answer! Pass throught the door!'
- setDoor(true)
- sleep(2)
- setDoor(false)
- end
- local function pulse(f,delay)
- f(true)
- sleep(delay)
- f(false)
- sleep(delay)
- end
- local wrong=function()
- reset_term()
- print'Wrong answer! Burn in hell!'
- local t=os.clock()
- while (os.clock()-t)<20 do
- pulse(setFire,1)
- end
- end
- function reset_term()
- term.clear()
- term.setCursorPos(1,1)
- end
- function copy(t)
- local c={}
- for i,v in pairs(t) do
- c[i]=v
- end
- return c
- end
- local get_permut
- do
- local permgen
- permgen = function (a, n, fn)
- if n == 0 then
- fn(a)
- else
- for i=1,n do
- -- put i-th element as the last one
- a[n], a[i] = a[i], a[n]
- -- generate all permutations of the other elements
- permgen(a, n - 1, fn)
- -- restore i-th element
- a[n], a[i] = a[i], a[n]
- end
- end
- end
- --- an iterator over all permutations of the elements of a list.
- -- Please note that the same list is returned each time, so do not keep references!
- -- @param a list-like table
- -- @return an iterator which provides the next permutation as a list
- function permute_iter (a)
- local n = #a
- local co = coroutine.create(function () permgen(a, n, coroutine.yield) end)
- return function () -- iterator
- local code, res = coroutine.resume(co)
- return res
- end
- end
- local function factorial(n)
- if n==0 then return 1 end
- return n*factorial(n-1)
- end
- function get_permut(t)
- local n=math.random()*(factorial(#t))
- local iter=permute_iter(t)
- for i=1,n-1 do iter() end
- local perm=iter()
- return perm
- end
- end
- function test()
- for i,question in pairs(questions) do
- reset_term()
- print(question[1])
- if type(question[2])=='string' then
- print'Type answer.'
- if question[2]~=read() then
- return false
- end
- else
- local permut=get_permut(copy(question[2]))
- for i,answer in pairs(permut) do
- print(i..')'..answer)
- end
- print'Press the number key matching the right answer'
- local id,ev,ok
- repeat
- local t={os.pullEvent()}
- ev,id=t[1],t[2]
- ok,id=pcall(tonumber,id)
- until (ev=='char' and type(id)=='number' and 1<=id and id<=#permut)
- if permut[id]~=question[2][1] then
- return false
- end
- end
- end
- return true
- end
- os.pullEvent=os.pullEventRaw
- wrong()
- repeat
- if test() then
- right()
- else
- wrong()
- end
- until false]===================],"CoolisOS/bin/programs/questions",[===================[local questions={
- {
- [[When was LG Founded??]],
- [[2000]],
- },
- {
- [[Who of the following is NOT a member of LG?]],
- {
- 'DarkRaven',
- 'FatherAbel',
- 'Tuglowz',
- 'Sanitty',
- },
- },
- {
- [[What is Legacy Gaming's website address minus the www]],
- [[legacygaming.com]],
- },
- {
- [[How Does LG Staff handle griefing?]],
- {
- 'All of these',
- 'With a Ban',
- 'With Jail',
- 'With Smite',
- 'None of these (except this one)',
- },
- },
- {
- [[What are you supposed to do if you see someone breaking the rules?]],
- {
- [[Message an Admin AND Email [email protected]]],
- [[Message an Admin]],
- [[Call the police]],
- [[Email [email protected]]],
- [[Message an Admin AND Call the police]],
- [[All of these]],
- },
- },
- {
- [[What is the Owner of LG's Minecraft Name?]],
- [[RoseD83]],
- },
- }
- return questions]===================],"CoolisOS/bin/programs/recharge.lua",[===================[--Recharger
- --log.setLevel('INFO','recharge')
- _log=log
- log=function(...) return _log('recharge',...) end
- args={...}
- tmax=args[1] and tonumber(args[1]) or 1800
- local Var=require'kernel.class.Var'
- var=Var('ect/recharger.var')
- local t=require'utils.turtle'
- local pdir=t.moves.forward
- local tdir=t.moves.down
- g1=pdir.detect
- g2=function() return turtle.getItemCount(1)==1 end
- g3=function() return turtle.getItemCount(2)==1 end
- select=turtle.select
- function out()
- log('INFO','outputing cell')
- select(2)
- repeat
- tdir.drop()
- if not(g3() and g2()) then break else sleep(1) end
- until false
- log('INFO','done')
- end
- function place()
- log('INFO','placing cell')
- select(1)
- repeat
- pdir.place()
- if (not g2()) and g1() then break else sleep(1) end
- until g1()
- log('INFO','done')
- end
- function remove()
- log('INFO','removing cell')
- select(2)
- t.clear(pdir)
- log('INFO','done')
- end
- local BREAK
- function main()
- sleep(5)
- repeat
- log('INFO','starting')
- if g1() and g2() then
- remove()
- end
- if g2() and g3() then
- out()
- sleep(5)
- end
- if g2() and not g3() then
- place()
- var.count=0
- end
- if g1() and not (g2() and g3())then
- log('INFO','recharging')
- var.count=var.count or 0
- tc=var.count
- ti=os.clock()
- select(1)
- repeat
- var.count=(os.clock()-ti)+tc
- print('now:'..var.count..';max='..tmax)
- sleep(1)
- tdir.suck()
- if g2() or BREAK then break end
- until var.count>tmax
- log('INFO','done')
- remove()
- end
- if g3() and not(g1() and g2()) then
- log('INFO','waiting for input')
- select(1)
- repeat
- tdir.suck()
- if not g2() then
- sleep(1)
- else
- break
- end
- until false
- log('INFO','done')
- end
- sleep(1)
- log('INFO','done')
- until false
- end]===================],"CoolisOS/bin/programs/test.lua",[===================[--CoolisOS/dev/test.lua
- -- r()
- -- function r()
- -- v=loadreq.reload'utils.Vec3'
- -- t=loadreq.reload'utils.turtle'
- -- end
- -- r()
- -- function g()
- -- shell.run('CoolisOS/dev/quarry.lua')
- -- m=recharge_machine
- -- end
- -- g()
- -- function r()
- -- w=loadreq.reload'dev.winding'
- -- iter=function(_,p) return w(p) end
- -- for p in iter,nil,{0,0} do
- -- print(p[1],',',p[2])
- -- os.pullEvent'key'
- -- end
- -- end
- -- sched.wait(sched.platform,'*')
- function b()
- printError(debug.traceback())
- end
- function a()
- b()
- end
- a()]===================],"CoolisOS/bin/programs/text",[===================[#white-black#&-
- Hello!
- #green-white#Green!
- ]===================],"CoolisOS/bin/programs/transpose.lua",[===================[while true do
- turtle.suckUp()
- while turtle.getItemCount(1)~=0 do
- turtle.drop()
- end
- sleep(1)
- end]===================],"CoolisOS/bin/programs/winding.lua",[===================[local max=math.max
- local abs=math.abs
- function winding(p)
- local x,y=p[1],p[2]
- local ax,ay=abs(x),abs(y)
- local d=max(ax,ay)
- if x==d and ay~=d then
- return {x,y+1}
- elseif y==d and x~=-d then
- return {x-1,y}
- elseif x==-d and y~=-d then
- return {x,y-1}
- elseif y==-d then
- return {x+1,y}
- elseif y==-d and x==d then
- return {x+1,y}
- else print(p[1],p[2]) _error'not a valid state' end
- end
- return winding
- ]===================],"CoolisOS/bin/utils",true,"CoolisOS/bin/utils/git.lua",[===================[local tArgs, gUser, gRepo, gPath, gBranch = {...}, nil, nil, "", "master"
- local usage = [[
- github <user> <repo> [path] [remote path] [branch]
- Remote path defaults to the root of the repo.
- Path defaults to the download folder.
- Branch defaults to master.
- If you want to leave an option empty use a dot.
- Example: github johnsmith hello-world . foo
- Everything inside the directory foo will be
- downloaded to downloads/hello-world/.
- ]]
- local blackList = [[
- @blacklistedfile
- ]]
- local title = "Github Repo Downloader"
- local fileList = {dirs={},files={}}
- local x , y = term.getSize()
- -- GUI
- function printTitle()
- local line = 2
- term.setCursorPos(1,line)
- for i = 2, x, 1 do write("-") end
- term.setCursorPos((x-title:len())/2,line+1)
- print(title)
- for i = 2, x, 1 do write("-") end
- end
- function writeCenter( str )
- term.clear()
- printTitle()
- term.setCursorPos((x-str:len())/2-1,y/2-1)
- for i = -1, str:len(), 1 do write("-") end
- term.setCursorPos((x-str:len())/2-1,y/2)
- print("|"..str.."|")
- term.setCursorPos((x-str:len())/2-1,y/2+1)
- for i = -1, str:len(), 1 do write("-") end
- end
- function printUsage()
- local str = "Press space key to continue"
- term.clear()
- printTitle()
- term.setCursorPos(1,y/2-4)
- print(usage)
- term.setCursorPos((x-str:len())/2,y/2+7)
- print(str)
- while true do
- local event, param1 = os.pullEvent("key")
- if param1 == 57 then
- sleep(0)
- break
- end
- end
- term.clear()
- term.setCursorPos(1,1)
- end
- -- Download File
- function downloadFile( path, url, name )
- writeCenter("Downloading File: "..name)
- dirPath = path:gmatch('([%w%_%.% %-%+%,%;%:%*%#%=%/]+)/'..name..'$')()
- if dirPath ~= nil and not fs.isDir(dirPath) then fs.makeDir(dirPath) end
- local content = http.get(url)
- local file = fs.open(path,"w")
- file.write(content.readAll())
- file.close()
- end
- -- Get Directory Contents
- function getGithubContents( path )
- local pType, pPath, pName, checkPath = {}, {}, {}, {}
- local response = http.get("https://api.github.com/repos/"..gUser.."/"..gRepo.."/contents/"..path.."/?ref="..gBranch)
- if response then
- response = response.readAll()
- if response ~= nil then
- for str in response:gmatch('"type":"(%w+)"') do table.insert(pType, str) end
- for str in response:gmatch('"path":"([^\"]+)"') do table.insert(pPath, str) end
- for str in response:gmatch('"name":"([^\"]+)"') do table.insert(pName, str) end
- end
- else
- writeCenter( "Error: Can't resolve URL" )
- sleep(2)
- term.clear()
- term.setCursorPos(1,1)
- error()
- end
- return pType, pPath, pName
- end
- -- Blacklist Function
- function isBlackListed( path )
- if blackList:gmatch("@"..path)() ~= nil then
- return true
- end
- end
- -- Download Manager
- function downloadManager( path )
- local fType, fPath, fName = getGithubContents( path )
- for i,data in pairs(fType) do
- if data == "file" then
- checkPath = http.get("https://raw.github.com/"..gUser.."/"..gRepo.."/"..gBranch.."/"..fPath[i])
- if checkPath == nil then
- fPath[i] = fPath[i].."/"..fName[i]
- end
- local path = "downloads/"..gRepo.."/"..fPath[i]
- if gPath ~= "" then path = gPath.."/"..gRepo.."/"..fPath[i] end
- if not fileList.files[path] and not isBlackListed(fPath[i]) then
- fileList.files[path] = {"https://raw.github.com/"..gUser.."/"..gRepo.."/"..gBranch.."/"..fPath[i],fName[i]}
- end
- end
- end
- for i, data in pairs(fType) do
- if data == "dir" then
- local path = "downloads/"..gRepo.."/"..fPath[i]
- if gPath ~= "" then path = gPath.."/"..gRepo.."/"..fPath[i] end
- if not fileList.dirs[path] then
- writeCenter("Listing directory: "..fName[i])
- fileList.dirs[path] = {"https://raw.github.com/"..gUser.."/"..gRepo.."/"..gBranch.."/"..fPath[i],fName[i]}
- downloadManager( fPath[i] )
- end
- end
- end
- end
- -- Main Function
- function main( path )
- writeCenter("Connecting to Github")
- downloadManager(path)
- for i, data in pairs(fileList.files) do
- downloadFile( i, data[1], data[2] )
- end
- writeCenter("Download completed")
- sleep(2,5)
- term.clear()
- term.setCursorPos(1,1)
- end
- -- Parse User Input
- function parseInput( user, repo , dldir, path, branch )
- if path == nil then path = "" end
- if branch ~= nil then gBranch = branch end
- if repo == nil then printUsage()
- else
- gUser = user
- gRepo = repo
- if dldir ~= nil then gPath = dldir end
- main( path )
- end
- end
- if not http then
- writeCenter("You need to enable the HTTP API!")
- sleep(3)
- term.clear()
- term.setCursorPos(1,1)
- else
- for i=1, 5, 1 do
- if tArgs[i] == "." then tArgs[i] = nil end
- end
- parseInput( tArgs[1], tArgs[2], tArgs[3], tArgs[4], tArgs[5] )
- end]===================],"CoolisOS/bin/utils/linked.lua",[===================[include'kernel.class'
- local pairs=pairs
- local print=print
- class.Linked()
- function Linked:__init(t)
- self.r,self.l={[0]=-1},{[-1]=0}
- if t then self:append_r(t) end
- end
- function Linked._insert(t,val,i_l,i_r)
- if t.r[val] then return end
- i_l,i_r=i_l or 0,i_r or -1
- --handle left right
- t.r[i_l], t.l[val], t.r[val], t.l[i_r]
- = val, i_l, i_r, val
- end
- function Linked.insert_l(t,val,i_l)
- return Linked._insert(t,val,i_l,t.r[i_l or 0])
- end
- function Linked.insert_r(t,val,i_r)
- return Linked._insert(t,val,t.l[i_r or -1],i_r)
- end
- function Linked.remove(t,val)
- val=val or t.r[0]
- if t.r[val] then t.r[t.l[val]],t.l[t.r[val]],t.r[val],t.l[val]=t.r[val],t.l[val],nil,nil end
- if val==-1 then val=nil end
- return val,t.r[val]
- end
- function Linked.next_r(t,ind)
- local _=t.r[ind or 0]
- if _~=-1 then return _ end
- end
- function Linked.next_l(t,ind)
- local _=t.r[ind or -1]
- if _~=0 then return _ end
- end
- function Linked.__tostring(t)
- s={}
- for i in self.next_r,t,nil do
- table.insert(s,tostring(i))
- end
- return 'Linked instance:'..table.concat(s,'<>')
- end
- function Linked:append_r(t)
- local old
- for i,v in ipairs(t) do
- self:insert_r(v,old)
- -- print(v,old)
- -- read()
- old=v
- end
- end
- function Linked:has(val)
- return self.r[val]~=nil
- end
- function Linked:isempty()
- return self.r[0]==-1
- end
- Linked.append=Linked.append_r
- Linked.push=Linked.insert_r
- Linked.pop=Linked.remove
- function Linked.test()
- t={'a',3,4}
- l=Linked(t)
- print(l)
- l:remove(3)
- print(l)
- print(l:has(4),l:has(3))
- return l
- end
- return Linked]===================],"CoolisOS/bin/utils/luau.lua",[===================[-- luau: Lua-upgraded
- -- Since we're sharing our scope with the user, make a table to keep
- -- all our stuff in, so we pollute the namespace as little as possible:
- luau_={}
- luau_.n = 0 -- current line number
- luau_.opt = {} -- options data
- luau_.mac = {} -- macros
- -- A default macro:
- luau_.mac.la = {['def']='os.loadAPI("%1")',['pnum']=1}
- -- Default option settings:
- luau_.opt.skip_lines={
- ['type'] = 'number',
- ['value'] = 0,
- ['default'] = 0,
- ['desc']='Number of blank lines to skip between input/output pairs.'
- }
- luau_.opt.use_color={
- ['type'] = 'boolean',
- ['value'] = true,
- ['default'] = true,
- ['desc']='Whether to use color, if available.'
- }
- -- Some variables we do want to expose:
- in_ = {} -- input history
- out_ = {} -- output history
- -- Help topics:
- luau_.help = {}
- luau_.help.summary = 'The following topics are available, and can be abbreviated--for instance, "/help sub" for "/help substitution".'
- luau_.help.substitution = 'Typing "s/find-expr/replace-expr/" will repeat the previously entered line, but with all occurrences of find-expr replaced by replace-expr. Lua\'s string-matching patterns may be used in find-expr. This is often quicker than using up-arrow to recall the command and editing it manually.'
- luau_.help.history = 'The tables in_ and out_ hold the input and output histories, and can be referenced, for example: "result=out_[9]".'
- luau_.help['repeat-command'] = 'To repeat a previous input, use the "!<n>" command. For example, typing !23 will re-evaluate the 23rd command in the history.'
- luau_.help['question-mark'] = 'The "?" character is a shortcut for print: "?foo" is short for "print(foo)". If foo is a table, then its keys and values will be displayed.'
- luau_.help['macros'] = 'You can define macros to shorten frequently-typed strings. For example, if you type "/macro %la=os.loadAPI", then in the future any occurence of "%la" in commands you type will be changed to "os.loadAPI" before processing by Lua.\n\nMacros can also accept parameters. For example, "/macro %rs=rednet.send(%1,%2)" will tell luau to expand "%rs[23][\'hi!\']" into "rednet.send(23,\'hi!\')".\n\nParameters may appear more than once and out of order--a silly example is "/macro %palindrome=%3%2%1%2%3", which expands "%palindrome[A][B][C]" into "CBABC".'
- luau_.help['commands'] = 'Luau has some built-in commands which are invoked by starting a line with the slash "/" character. Type "/commands" for a list.'
- -- Build a sorted list of help topics:
- luau_.helpkeys = {}
- for k in pairs(luau_.help) do
- if k~='summary' then
- table.insert(luau_.helpkeys, k)
- end
- end
- table.sort(luau_.helpkeys)
- -- Define the appropriate color function:
- if term.isColor() then
- term.setTextColor(colors.gray)
- luau_.setColor = function(c)
- if luau_.opt.use_color.value then
- term.setTextColor(c)
- else
- term.setTextColor(gray)
- end
- end
- else
- luau_.setColor = function(c) return end
- end
- luau_.terminated = false
- -- End of initialization code.
- function luau_.terminate()
- -- Goodbye, cruel world:
- luau_.terminated = true
- end
- function luau_.separator()
- -- A line of dashes, of the appropriate length:
- return(string.rep('-', term.getSize()))
- end
- function luau_.loadData()
- -- Load config options and macros:
- local h
- if fs.exists('luau.conf') then
- h = fs.open('luau.conf','r')
- local opt, mac
- opt = textutils.unserialize(h.readLine())
- mac = textutils.unserialize(h.readLine())
- h.close()
- if type(opt)=='table' and type(mac)=='table' then
- luau_.opt = opt
- luau_.mac = mac
- return true
- else
- luau_.showError('Unable to load configuration; /luau.conf is corrupt. Exiting. (Delete or rename /luau.conf to start fresh)')
- return false
- end
- else
- luau_.setColor(colors.blue)
- print('Configuration file /luau.conf does not exist; creating with default values.')
- luau_.saveData()
- return true
- end
- end
- function luau_.saveData()
- -- Save config options and macros:
- h = fs.open('luau.conf','w')
- h.writeLine(textutils.serialize(luau_.opt))
- h.writeLine(textutils.serialize(luau_.mac))
- h.close()
- end
- function luau_.firstMatch(s, t)
- for i, k in ipairs(t) do
- if string.find(k, s) == 1 then
- return k
- end
- end
- return nil
- end
- function luau_.showError(errorText)
- -- Report errors to the user
- luau_.setColor(colors.red)
- print(errorText)
- luau_.setColor(colors.gray)
- end
- function luau_.trim(s)
- -- Trim leading/trailing spaces from string s:
- return (s:gsub("^%s*(.-)%s*$", "%1"))
- end
- function luau_.macro(s)
- -- !<n> to re-evaluate n-th input:
- local a, b, n
- a, b, n = s:find('!(%d+)')
- n = tonumber(n)
- if a == 1 then
- if in_[n] then
- s = in_[n]
- else
- luau_.showError("History entry "..tostring(n).." not found.")
- s = ''
- end
- return s
- end
- -- s/foo/bar/ substitutions
- a, b, find, replace, flags = s:find('s/(.+)/(.*)/(.*)')
- if find and in_[luau_.n] then
- s = in_[luau_.n-1]:gsub(find, replace)
- return s
- end
- -- Macro expansion:
- local lMark, rMark = '>tboss>', '<tboss<'
- -- This lMark/rMark business is an ugly hack that wouldn't be
- -- necessary if I knew how to directly capture the stuff INSIDE a
- -- balanced expression captured with %b[], i.e. without the brackets.
- for name, macro in pairs(luau_.mac) do
- local pattern = '%%'..name..string.rep('(%b[])',macro.pnum)
- -- This is a workaround for a bug in the string library:
- local s_ = s..string.rep('[ ]',macro.pnum)
- local a, b = s_:find(pattern)
- if b and b<=s:len() then
- if s_:find(pattern) then
- local def = macro.def:gsub('(%%%d)',lMark..'%1'..rMark)
- s_ = s_:gsub(pattern, def)
- s_ = s_:gsub(lMark..'%[', '')
- s_ = s_:gsub('%]'..rMark, '')
- s = s_:sub(1, s_:len()-3*macro.pnum)
- end
- end
- end
- return s
- end
- function luau_.columnList(t)
- -- Print the elements of t in columns:
- -- Compute the max length of the names so we can decide
- -- how many columns to print them in:
- local maxLen = 0
- for i,k in ipairs(t) do
- if string.len(k) > maxLen then maxLen = string.len(k) end
- end
- local spacing = 2 -- Spacing between columns
- local w, h = term.getSize()
- local nCols = math.floor((w+spacing)/(maxLen+spacing))
- -- Print all the names in columns:
- for i, k in ipairs(t) do
- if (i%nCols == 0) or (i == #t) then
- print(k)
- else
- write(k..string.rep(' ', maxLen+spacing-string.len(k)))
- end
- end
- end
- function luau_.showHelp(topic)
- luau_.setColor(colors.blue)
- topic = tostring(topic)
- if topic ~= '' then
- -- Do partial matching:
- local mtopic = luau_.firstMatch(topic, luau_.helpkeys)
- if luau_.help[mtopic] then
- local m, n = term.getSize()
- textutils.pagedPrint('('..mtopic..') '..luau_.help[mtopic],n-3)
- else
- print('No help found for "'..topic..'". Type "help" for a list of topics.')
- end
- else
- -- Print help summary and list of topics:
- print(luau_.help.summary)
- print(luau_.separator())
- --luau_.setColor(colors.lightBlue)
- luau_.columnList(luau_.helpkeys)
- end
- end
- function luau_.searchHistory(s)
- -- Print a list of history entries matching s.
- -- Should refactor this into a search and a print.
- local nFound = 0
- for i,v in ipairs(in_) do
- if string.find(v,s) then
- print(i..': '..v)
- nFound = nFound+1
- end
- end
- return nFound
- end
- function luau_.keySort(a, b)
- -- Order function for table keys, returns true if a<b:
- if type(a)=='number' and type(b)=='number' then
- return a<b
- elseif type(a)=='number' then
- return true
- elseif type(b)=='number' then
- return false
- elseif type(a)=='string' and type(b)=='string' then
- return a<b
- elseif type(a)=='string' then
- return true
- elseif type(b)=='string' then
- return false
- else
- return (type(a)<type(b))
- end
- end
- function luau_.serial(v)
- -- Wrapper for textutils.serialize, to work with all types
- if string.find("nil boolean number string table", type(v)) then
- local err, str = pcall(function() textutils.serialize(v) end)
- if err then str=tostring(v) end
- return str
- else
- return tostring(v)
- end
- end
- function luau_.setMacro(p, overwrite)
- -- Define or redefine a macro:
- luau_.setColor(colors.yellow)
- if p=='' then
- -- Display currently defined macros:
- print("The following macros are currently defined:")
- print(luau_.separator())
- for k in pairs(luau_.mac) do
- print("%"..k.."="..luau_.mac[k].def)
- end
- else
- -- Should be in the form "%name=def":
- local a, b, name, def = p:find('%%([%w_]+)=(.*)')
- if name then
- -- See if it's already defined:
- if luau_.mac[name] and not overwrite then
- print('Macro %'..name..' is already defined. Use /Macro to overwrite it.')
- return false
- end
- -- See how many params:
- local n = 0
- def:gsub('%%(%d+)', function(s) n=tonumber(s) end)
- luau_.mac[name] = {['def']=def, ['pnum']=n}
- -- Save it right away. We could wait until we exit, but
- -- during development crashes are likely so let's not
- -- lose people's work.
- luau_.saveData()
- else
- luau_.showError('Invalid macro definition (see /help macro).')
- return false
- end
- end
- return true
- end
- function luau_.setOpt(p)
- luau_.setColor(colors.yellow)
- if p=='' then
- print('The following options are available. Type "/option <option_name>=<value>" to change, or "/option <option_name>" to get a description.')
- local keys = {}
- for k in pairs(luau_.opt) do keys[#keys+1]=k end
- table.sort(keys, luau_.keySort)
- print(luau_.separator())
- for i, k in ipairs(keys) do
- print(k..'='..tostring(luau_.opt[k].value))
- end
- elseif p:find('=') then
- local a, b, opt, val = p:find("([%w_]+)%s*=%s*(.*)")
- if luau_.opt[opt] then
- -- Make sure the types are right:
- local t = luau_.opt[opt].type
- local ok = false
- if t=='boolean' then
- local v = val:lower()
- if v=='true' or v=='1' or v=='yes' or v=='y' then
- luau_.opt[opt].value = true
- ok = true
- elseif v=='false' or v=='0' or v=='no' or v=='n' then
- luau_.opt[opt].value = false
- ok = true
- end
- elseif t=='number' then
- local v = tonumber(val)
- if v then
- luau_.opt[opt].value = v
- ok = true
- end
- elseif t=='string' then
- luau_.opt[opt].value = val
- ok = true
- end
- if ok then
- luau_.saveData()
- else
- print('Value for '..opt..' must be of type '..t..'.')
- end
- else
- print('Option "'..p..'" not found. Type "/option" for a list.')
- end
- else
- if luau_.opt[p] then
- print(luau_.opt[p].desc..' [type='..luau_.opt[p].type..', value='..tostring(luau_.opt[p].value)..', default='..tostring(luau_.opt[p].default)..']')
- else
- print('Option "'..p..'" not found. Type "/option" for a list.')
- end
- end
- end
- function luau_.showCommands(p)
- luau_.setColor(colors.blue)
- if p=='' then
- print('The following commands are available. Type "/command <command-name>" for details.')
- print(luau_.separator())
- luau_.columnList(luau_.cmdNames)
- else
- local cmd = luau_.firstMatch(p, luau_.cmdNames)
- if cmd then
- print('/'..cmd..': '..luau_.cmd[cmd].desc)
- else
- print('Command "'..p..'" not found. Type "/commands" for a list.')
- end
- end
- end
- -- Now that we've defined all our handler functions, we can set up the command list:
- luau_.cmd = {}
- luau_.cmd.help = {
- ['handler'] = luau_.showHelp,
- ['desc'] = 'Online help. Type "/help <topic>", or "/help" for a list of topics.',
- }
- luau_.cmd.exit = {
- ['handler'] = luau_.terminate,
- ['desc'] = 'Exits Luau and returns you to the shell or wherever else you launched it from.',
- }
- luau_.cmd.history = {
- ['handler'] = luau_.searchHistory,
- ['desc'] = 'Typing "/history" will simply print the entire history buffer, while "/history <pattern>" will only show entries matching <pattern>.',
- }
- luau_.cmd.macro = {
- ['handler'] = function(p) luau_.setMacro(p, false) end,
- ['desc'] = 'Define a text-expansion macro with "/macro %<name>=<output>", or type "/macro" for a list of existing macros. Type "/help macros" for more details.',
- }
- luau_.cmd.Macro = {
- ['handler'] = function(p) luau_.setMacro(p, true) end,
- ['desc'] = 'Same as "/macro", but able to overwrite existing definitions.',
- }
- luau_.cmd.options = {
- ['handler'] = luau_.setOpt,
- ['desc'] = 'Type "/options" for a list of all options, "/option <name>" for an option\'s description, or "/option <name>=<value>" to change it.',
- }
- luau_.cmd.commands = {
- ['handler'] = luau_.showCommands,
- ['desc'] = 'Type "/commands" for a list of commands, or "/command <command-name>" to view a description of a particular one.',
- }
- luau_.cmdNames = {}
- for k in pairs(luau_.cmd) do table.insert(luau_.cmdNames, k) end
- table.sort(luau_.cmdNames)
- function luau_.go()
- -- Load config options and macros:
- if not luau_.loadData() then return false end
- luau_.setColor(colors.blue)
- print()
- print('LUAU--LUA Upgraded v0.1 by Tinyboss\nType "/exit" to exit, "/help" for help.')
- while not luau_.terminated do
- luau_.setColor(colors.gray)
- for i=1,luau_.opt.skip_lines.value do print() end
- luau_.n = luau_.n+1
- -- Get the next input, passing the input history for recall by up-arrow:
- local input = ''
- while luau_.trim(input)=='' do
- luau_.setColor(colors.lime)
- write(' in_['..luau_.n..']: ')
- luau_.setColor(colors.lightGray)
- input = luau_.trim(read(false, in_,getfenv(1)))
- end
- in_[luau_.n] = input
- luau_.handled = false
- -- ? is a shortcut for print, with benefits:
- if in_[luau_.n]:sub(1,1) == "?" then
- rest_ = luau_.trim(in_[luau_.n]:sub(2))
- if rest_ == "" then
- -- If they just typed "?", they probably wanted help:
- luau_.setColor(colors.blue)
- print('"?foo" is a synonym for "print(foo)". Use "/help" for help.')
- luau_.handled = true
- elseif type(getfenv(1)[rest_])=="table" then
- -- If it's a table, display it:
- local keys = {}
- for k in pairs(getfenv(1)[rest_]) do
- keys[#keys+1]=k
- end
- table.sort(keys, luau_.keySort)
- local kv = {}
- for i,k in ipairs(keys) do
- kv[i] = luau_.serial(k)..'='
- ..luau_.serial(getfenv(1)[rest_][k])
- end
- print('{',table.concat(kv, ', '),'}')
- luau_.handled = true
- else
- in_[luau_.n] = 'print('..in_[luau_.n]:sub(2)..')'
- end
- end
- if not luau_.handled then
- -- See if it's a slash command:
- local a_, b_, cmd_, rest_ = in_[luau_.n]:find('/(%a+)%s*(.*)')
- if a_==1 then
- cmd_ = luau_.firstMatch(cmd_, luau_.cmdNames)
- if cmd_ then
- luau_.cmd[cmd_].handler(rest_)
- else
- luau_.setColor(colors.yellow)
- print('Invalid command. (Try "/help".)')
- end
- else
- -- Do macro substitution before passing it to Lua:
- in_[luau_.n] = luau_.macro(in_[luau_.n])
- luau_.setColor(colors.gray)
- is_print_ = string.sub(in_[luau_.n], 1, 6)=='print('
- -- Try it as an expression first:
- func_, err_ = loadstring('out_[luau_.n]='..in_[luau_.n])
- if err_ then
- -- Try it as a statement (e.g. "pi=3"):
- func_, err_ = loadstring(in_[luau_.n])
- if err_ then
- luau_.showError('(loadstring)'..err_)
- else
- setfenv(func_, getfenv(1))
- success_, err_ = pcall(func_)
- if not success_ then
- luau_.showError(err_)
- end
- end
- else
- -- It worked as an expression, so store and show the result:
- setfenv(func_, getfenv(1))
- success_, err_ = pcall(func_)
- if success_ then
- if out_[luau_.n] and not is_print_ then
- luau_.setColor(colors.green)
- write('out_['..luau_.n..']: ')
- luau_.setColor(colors.gray)
- print(out_[luau_.n])
- end
- else
- luau_.showError(err_)
- end
- end
- end
- end
- end
- -- Save config and macro data:
- luau_.saveData()
- end
- luau_.go()]===================],"CoolisOS/bin/utils/path.lua",[===================[---
- -- Path utils.
- --
- -- **Definition of path** used by this file:
- --
- -- - it is **not Lua path**, Lua path meaning the way Lua provide table indexing.
- -- - especially, **'table[ ]'** notation is not supported
- -- - **element separator is '.'** (dot)
- -- - provided **path is always cleaned** before being processed:
- -- - if the path starts and/or ends with one or several dots, there are automatically removed.<br />
- -- e.g.: `'...toto.tutu....'` means `'toto.tutu'`
- -- - internal repetitions of successives dots are replaced by a single dot.<br />
- -- e.g.: `'toto...tutu.tata..foo.bar'` means `'toto.tutu.tata.foo.bar'`
- -- - if a path element can be **converted to a number**, then it is returned as a number,
- -- otherwise all elements or paths are returned as strings.<br />
- -- e.g.: `split('a.2', 1)` -> `('a', 2)` (where 2 is returned as a number) <br />
- -- e.g.: `split('a.b.2e3.c', 1)` -> `('a', 'b.2000.c')`
- --
- -- @module utils.path
- --
- local check = require"kernel.checker".check
- local M = { }
- --public API
- local split,clean,segments, get, set, gsplit, concat, find
- local function pathconcat(pt, starti, endi)
- local t = {}
- local prev
- local empties = 0
- starti = starti or 1
- endi = endi or #pt
- for i = starti, endi do
- local v = pt[i]
- if not v then break
- elseif v == '' then
- empties = empties+1
- else
- table.insert(t, prev)
- prev = v
- end
- end
- table.insert(t, prev)
- --log('PATH', 'INFO', "pathconcat(%s, %d, %d) generates table %s, wants indexes %d->%d",
- -- sprint(pt), starti, endi, sprint(t), 1, endi-starti+1-empties)
- return table.concat(t, '.', 1, endi-starti+1-empties)
- end
- --------------------------------------------------------------------------------
- -- Concatenates a sequence of path strings together.
- --
- -- @function [parent=#utils.path] concat
- -- @param varargs list of strings to concatenate into a valid path
- -- @return resulting path as a string
- --
- function concat(...)
- return pathconcat({...})
- end
- --------------------------------------------------------------------------------
- -- Cleans a path.
- --
- -- Removes trailing/preceding/doubling '.'.
- --
- -- @function [parent=#utils.path] clean
- -- @param path string containing the path to clean.
- -- @return cleaned path as a string.
- --
- function clean(path)
- check('string',path)
- local p = segments(path)
- return pathconcat(p)
- end
- --------------------------------------------------------------------------------
- -- Sets a value in a tree-like table structure.
- --
- -- The value to set is indicated by the path relative to the table.
- -- This function creates the table structure to store the value, unless the value to set is nil.
- -- If the value to set is nil and the table structure already exists then the value is set to nil.
- -- If the value is not nil, then the table structure is always created/overwritten and the value set.
- --
- -- @function [parent=#utils.path] set
- -- @param t table where to set the value.
- -- @param path can be either a string (see @{#(utils.path).split})
- -- or an array where path[1] is the root and path[n] is the leaf.
- -- @param value the value to set.
- --
- function set(t, path, value)
- check('table,string|table',t, path)
- local p = type(path)=='string' and segments(path) or path
- local k = table.remove(p)
- local t = find(t, p, value~=nil) -- only create the table structure if the value to set is non nil!
- -- local t = findtable(t, p, true) -- for the creation of the table structure, even if the value to set is non nil!
- if t then t[k] = value end
- end
- --------------------------------------------------------------------------------
- -- Gets the value of a table field. The field can be in a sub table.
- --
- -- The field to get is indicated by a path relative to the table.
- --
- -- @function [parent=#utils.path] get
- -- @param t table where to set the value.
- -- @param path can be eiher a string (see @{split}) or an array where path[1] is the root and path[n] is the leaf.
- -- @return value if the field is found.
- -- @return nil otherthise.
- --
- function get(t, path)
- check('table,string|table',t,path)
- local p = type(path)=='string' and segments(path) or path
- local k = table.remove(p)
- if not k then return t end
- local t = find(t, p)
- return t and t[k]
- end
- --------------------------------------------------------------------------------
- -- Enumerates path partitions in a for-loop generator, starting from the right.
- --
- -- For instance, `gsplit "a.b.c"` will generate successively
- -- `("a.b.c", ""), ("a.b", "c"), ("a", "b.c"), ("", "a.b.c")`.
- --
- -- @function [parent=#utils.path] gsplit
- -- @param path the path as a string
- -- @return the for-loop iterator function
- --
- function gsplit (path)
- check ('string',path)
- local segs = segments(path)
- local nsegs = #segs
- local limit = nsegs
- local function f()
- if limit == -1 then return nil, nil end
- local a, b = pathconcat(segs, 1, limit), pathconcat(segs, limit+1, nsegs)
- limit = limit - 1
- return a, b
- end
- return f
- end
- --------------------------------------------------------------------------------
- -- Splits a path into two halves, can be used to get path root, tail etc.
- --
- -- The content of the two halves depends on 'n' param: there will be `n` segments in the first half if `n>0`,
- -- `-n` segments in the second half if `n<0`.
- --
- -- If there are less then `n` segments, returns the path argument followed by
- -- an empty path.
- --
- -- If there are less then `-n` segments, returns an empty path followed by the
- -- path argument.
- --
- -- Note that if a half is a single element and that this element can be converted into a number,
- -- it is returned as a number.
- --
- -- @function [parent=#utils.path] split
- -- @param path the path as a string
- -- @param n number defining how the path is splitted (see above description).
- -- @return the two halves
- -- @usage local root, tail = split('a.b.c', 1)
- -- ->root contains 'a', tail contains 'b.c'
- --
- function split(path, n)
- check('string,number',path,n)
- local segments = segments(path)
- if n>#segments then return path, ''
- elseif -n>#segments then return '', path
- else
- if n<0 then n=#segments+n end
- return pathconcat(segments, 1, n), pathconcat(segments, n+1, #segments)
- end
- end
- --------------------------------------------------------------------------------
- -- Splits a path into segments.
- --
- -- Each segment is delimited by '.' pattern.
- --
- -- @function [parent=#utils.path] segments
- -- @param path string containing the path to split.
- -- @return list of split path elements.
- --
- function segments(path)
- check('string',path)
- local t = {}
- local index, newindex, elt = 1
- repeat
- newindex = path:find(".", index, true) or #path+1 --last round
- elt = path:sub(index, newindex-1)
- elt = tonumber(elt) or elt
- if elt and elt ~= "" then table.insert(t, elt) end
- index = newindex+1
- until newindex==#path+1
- return t
- end
- --------------------------------------------------------------------------------
- -- Retrieves the element in a sub-table corresponding to the path.
- --
- -- @function [parent=#utils.path] find
- -- @param t is the table to look into.
- -- @param path can be either a string (see @{segments}) or an array where
- -- `path[1]` is the root and `path[n]` is the leaf.
- -- @param force parameter allows to create intermediate tables as specified
- -- by the path, if necessary.
- -- @return returned values depend on force value:
- --
- -- * if force is false (or nil), find returns the table if it finds one,
- -- or it returns nil followed by the subpath that points to non table value
- --
- -- * if force is true, find overwrites or create tables as necessary so
- -- it always returns a table.
- --
- -- * if force is 'noowr', find creates tables as necessary but does not
- -- overwrite non-table values. So as with `force=false`, it only returns a
- -- table if possible, and nil followed by the path that points to the first
- -- neither-table-nor-nil value otherwise.
- --
- -- @usage config = {toto={titi={tutu = 5}}}
- -- find(config, "toto.titi") -- will return the table titi
- --
- function find(t, path, force)
- check('table,string|table', t,path)
- path = type(path)=="string" and segments(path) or path
- for i, n in ipairs(path) do
- local v = t[n]
- if type(v) ~= "table" then
- if not force or (force=="noowr" and v~=nil) then return nil, pathconcat(path, 1, i)
- else v = {} t[n] = v end
- end
- t = v
- end
- return t
- end
- --public API
- M.split=split; M.clean=clean; M.segments=segments; M.get=get; M.set=set;
- M.gsplit=gsplit; M.concat=concat; M.find=find
- return M
- ]===================],"CoolisOS/bin/utils/print.lua",[===================[
- local io=io
- local string=string
- local table=table
- local type=type
- local tostring=tostring
- local pairs=pairs
- local print=print
- local getmetatable=getmetatable
- local select=select
- env=getfenv()
- setmetatable(env,nil)
- mtostring=function(...)
- local t={}
- for i=1,select('#',...) do
- table.insert(t,tostring(select(i,...)))
- end
- return table.concat(t,';')
- end
- -- Make sure stdout is actually flushed every line
- if io and io.stdout then
- io.stdout:setvbuf("line")
- end
- local function quotestring(x)
- local function q(k) return
- k=='"' and '\\"' or
- k=='\\' and '\\\\' or
- string.format('\\%03d', k :byte())
- end
- return '"' .. x :gsub ('[%z\1-\9\11\12\14-\31\128-\255"\\]', q) .. '"'
- end
- ------------------------------------------------------------------------------
- -- virtual printer: print a textual description of the objects in `...', using
- -- function `write()' as a writer. By providing the appropriate writer, one
- -- can make this function write on a TCP channel, in a string,... anywhere.
- -- print_indent is the number of indentation characters used to render
- -- tables. If nil/false, tables are rendered without carriage returns.
- ------------------------------------------------------------------------------
- function vprint (write, print_indent, ...)
- local in_progress = { }
- local indent_level = 0
- local tinsert = table.insert
- local function aux(x)
- local t=type(x)
- if t=="string" then write(quotestring(x))
- elseif t=="boolean" then write(x and "true" or "false")
- elseif t=="number" then write(tostring(x))
- elseif t=="nil" then write("nil")
- elseif t=="table" then
- if getmetatable(x) and getmetatable(x).__tostring then write(tostring(x))
- elseif in_progress[x] then write(tostring(table)) else
- in_progress[x] = true
- indent_level = indent_level+1
- local function print_separator()
- if print_indent then write(",\r\n" .. (" "):rep(print_indent * indent_level))
- else write(", ") end
- end
- -- number of elements according to pairs(): zero, one, many
- local at_least_one, more_than_one = false, false
- for _ in pairs(x) do
- if at_least_one then more_than_one=true; break
- else at_least_one=true end
- end
- if not at_least_one then write "{ }"; return
- elseif not more_than_one or not print_indent then write "{ "
- else write("{\r\n" .. (" "):rep(print_indent * indent_level)) end
- local list_keys = { }
- local last_list_key = 0
- repeat
- local i = last_list_key + 1
- local occupied = x[i]~=nil
- if occupied then list_keys[i]=true; last_list_key=i end
- until not occupied
- for i=1, last_list_key do
- if i>1 then print_separator() end
- aux(x[i])
- end
- local first_hash_pair = true
- for k, v in pairs(x) do
- if not list_keys[k] then -- don't reprint the list-part
- -- separator
- if not first_hash_pair or last_list_key~=0 then print_separator() end
- first_hash_pair = false
- -- key
- if type(k)=="string" and k:match"^[%a_][%w_]*$" then write(k .. " = ")
- else write("["); aux(k); write("] = ") end
- -- value
- aux(v)
- end
- end
- write (" }")
- indent_level = indent_level-1
- in_progress[x] = nil
- end
- else write(tostring(x)) end
- end
- local args = {...}
- local nb = table.getn(args)
- for k = 1, nb do aux(args[k]); if k<nb then write "\t" end end
- end
- ------------------------------------------------------------------------------
- -- A more precise print (on stdout)
- ------------------------------------------------------------------------------
- function p (...)
- local out = {}
- local function write(s)
- table.insert(out, s)
- end
- vprint(write, 3, ...)
- print(table.concat(out))
- end
- ------------------------------------------------------------------------------
- -- Build up and return a string instead of printing in some channel.
- ------------------------------------------------------------------------------
- function siprint(indent, ...)
- local ins, acc = table.insert, { }
- vprint(function(x) return ins(acc, x) end, indent, ...)
- return table.concat(acc)
- end
- function sprint(...)
- return siprint(false, ...)
- end
- function printf(...) return print(string.format(...)) end
- return env]===================],"CoolisOS/bin/utils/serpent.lua",[===================[local n, v = "serpent", 0.224 -- (C) 2012-13 Paul Kulchenko; MIT License
- local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
- local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
- local badtype = {thread = true, userdata = true}
- local keyword, globals, G = {}, {}, (_G or _ENV)
- for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
- 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
- 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
- for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
- for _,g in ipairs({'coroutine', 'io', 'math', 'string', 'table', 'os'}) do
- for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end
- local function s(t, opts)
- local name, indent, fatal,dname = opts.name, opts.indent, opts.fatal,opts.debug
- local sparse, custom, huge,dbg = opts.sparse, opts.custom, not opts.nohuge,opts.debug
- local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
- local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
- local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
- local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
- -- tostring(val) is needed because __tostring may return a non-string value
- function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end)) end
- local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s)
- or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
- or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
- local function comment(s,l)
- return comm and (l or 0) < comm and ' --[['..(dbg and type(s)~='string' and debug.getinfo(s) or tostring(s))..']]' or '' end
- local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
- and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
- local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
- local n = name == nil and '' or name
- local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
- local safe = plain and n or '['..safestr(n)..']'
- return (path or '')..(plain and path and '.' or '')..safe, safe end
- local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding
- local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
- local function padnum(d) return ("%0"..maxn.."d"):format(d) end
- table.sort(k, function(a,b)
- -- sort numeric keys first: k[key] is non-nil for numeric keys
- return (k[a] and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
- < (k[b] and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
- local function val2str(t, name, indent, insref, path, plainindex, level,pass)
- local ttype, level, mt = type(t), (level or 0), getmetatable(t)
- local spath, sname = safename(path, name)
- local tag = plainindex and
- ((type(name) == "number") and '' or name..space..'='..space) or
- (name ~= nil and sname..space..'='..space or '')
- if seen[t] then -- already seen this element
- if dbg then return tag..comment(t,level) end
- table.insert(sref, spath..space..'='..space..seen[t])
- return tag..'nil'..comment('ref'..tostring(t), level) end
- if mt and not dbg then
- if mt.__serialize or mt.__tostring then -- knows how to serialize itself
- seen[t] = insref or spath
- if rawget(mt,'__serialize') then t = mt.__serialize(t) else t = tostring(t) end
- ttype = type(t) -- new value falls through to be serialized
- end
- end
- if ttype == "table" then
- if level >= maxl then return tag..'{}'..comment('max', level) end
- seen[t] = insref or spath
- if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
- local maxn, o, out = #t, {}, {}
- for key = 1, maxn do table.insert(o, key) end
- for key in pairs(t) do if not o[key] then table.insert(o, key) end end
- if opts.sortkeys then alphanumsort(o, t, opts.sortkeys) end
- for n, key in ipairs(o) do
- local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
- if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing
- or opts.keyallow and not opts.keyallow[key]
- or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types
- or sparse and value == nil then -- skipping nils; do nothing
- elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then
- if dbg then
- local s
- if not (seen[key] or globals[key]) then
- seen[key]=debug.getinfo(key)
- s = '['..(val2str(key,nil,indent,nil,nil,false,level+1))..']'..comment(key,level)
- end
- table.insert(out,val2str(value,s or (seen[key] or globals[key]),indent,insref,nil,true,level+1))
- else
- if not seen[key] and not globals[key] then
- local sname = safename(iname, gensym(key)) -- iname is table for local variables
- sref[#sref] = val2str(key,sname,indent,sname,iname,true)
- end
- table.insert(sref, 'placeholder')
- local path = seen[t]..'['..(seen[key] or globals[key] or gensym(key))..']'
- sref[#sref] = path..space..'='..space..(seen[value] or val2str(value,nil,indent,path))
- end
- else
- table.insert(out,val2str(value,key,indent,insref,seen[t],plainindex,level+1))
- end
- end
- local prefix = string.rep(indent or '', level)
- local head = indent and '{\n'..prefix..indent or '{'
- local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
- local tail = indent and "\n"..prefix..'}' or '}'
- return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level)
- elseif badtype[ttype] then
- seen[t] = insref or spath
- return tag..globerr(t, level)
- elseif ttype == 'function' and not dbg then
- seen[t] = insref or spath
- local ok, res = pcall(string.dump, t)
- local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or
- "loadstring("..safestr(res)..",'@serialized')")..comment(t, level))
- return tag..(func or globerr(t, level))
- else return tag..safestr(t) end -- handle all other types
- end
- local sepr = indent and "\n" or ";"..space
- local body = val2str(t, name, indent) -- this call also populates sref
- local tail = #sref>1 and table.concat(sref, sepr)..sepr or ''
- return not name and body or "do local "..body..sepr..tail.."return "..name..sepr.."end"
- end
- local function merge(a, b) if b then for k,v in pairs(b) do a[k] = (a[k]==nil and v or a[k]) end end; return a; end
- return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
- dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
- line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
- block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }]===================],"CoolisOS/bin/utils/turtle.lua",[===================[
- --[[
- process:
- -state
- -state handler
- --interrupts
- -state machines?
- -complex for generalism
- -simple for dig queues: <- are generally stateless
- are kept separately, dig queues are just vector coords
- position and orientation->state
- turtle api overwritten to catch movement
- interpreter command to set position, world-wide
- implies keepig initial referencial
- simple wrap-up of goto
- (need stack like state machines for pausing, and then returning)
- -general pathfinding
- djikstra algorithm
- maintainig map
- - imprredictable movement
- + can contornate obstacles
- ]]
- --state_stack={}
- --
- --
- --
- --function get_move_queue()
- -- local q={}
- -- for i=1,10 do
- -- table.insert(function() goto(Vec3(i,0,0)) end)
- -- end
- -- return q
- --end
- --
- --[[doing queue implies
- --getting state/resuming state
- --executing and saving <- open new, save current, delete old
- --goto needs some context
- --turtle movement is single -coroutined; means that mode can be global
- --]]
- --function get_coords()
- -- f=fs.open('ect/pos','r')
- -- pos=Vec3(textutils.deserialize(f.readAll()))
- -- f.close()
- -- return pos
- --end
- --function save_pos(pos)
- -- f=fs.open('ect/pos','w')
- -- f.writeLine(pos:serialize())
- -- f.close()
- --end
- --
- --
- --function get_state_for(queue)
- -- local coords=get_coords()
- -- for i,v in pairs(queue) do
- -- if v==coords then return i end
- -- end
- --end
- --function do_queue(queue)
- -- cur=get_state_for(queue)
- -- for i=cur,#queue do
- -- goto(queue[i])
- -- end
- --end
- --
- --function main()
- -- mov_queue=get_move_queue()
- -- do_queue(queue)
- --end
- if not turtle then error('not a turtle',2) end
- if turtle._old then
- turtle=util.shcopy(turtle._old)
- else
- turtle._old=util.shcopy(turtle)
- end
- local _log=log
- local log=function(...) return _log('turtle',...) end
- include'kernel.class'
- local Vec3=require'utils.Vec3'
- local util=require'kernel.util'
- local Var=require'kernel.class.Var'
- ---forward declaration
- local api
- --persistence
- local pos,dir
- local saveDir=fs.combine(_INSTALL_PATH,'ect/turtle')
- if not fs.exists(saveDir) then fs.makeDir(saveDir) end
- local dump=require'utils.serpent'.dump
- local var=Var(fs.combine(saveDir,'pos-dir.var'))
- local mode=Var(fs.combine(saveDir,'mode.var'))
- local function merge(dst,src)
- for i,v in pairs(src) do
- if dst[i]==nil then
- dst[i]=src[i]
- end
- end
- end
- merge(mode,{
- dig=true,
- attack=true,
- count=20,
- delay=0.5,
- gps=false,
- })
- local function save()
- var.pos=pos
- if dir==Vec3() then error'zero dir' end
- var.dir=dir
- end
- local function load()
- pos,dir=Vec3(var.pos),Vec3(var.dir)
- end
- function set(_pos,_dir)
- pos,dir=(_pos or pos):clone(),(_dir or dir):clone()
- save()
- end
- function get() return pos:clone(),dir:clone() end
- local function sign(num)
- return (num==nil or num==0) and 0 or (num>0 and 1 or -1)
- end
- --fuel handling
- function ensureFuel()
- if api.ensureFuel then api.ensureFuel() end
- end
- ---
- --atomic overrides of the turtle api and moves def
- moves={}
- do
- local s_dirs={
- up={
- vec=function() return Vec3(0,1,0) end,
- at=function(at)
- at.y=at.y+1
- end,
- raw_name="up",
- attack=turtle.attackUp,
- suck=turtle.suckUp,dig=turtle.digUp, raw_move=turtle.up, detect=turtle.detectUp, place=turtle.placeUp,compare=turtle.compareUp, drop=turtle.dropUp,},
- forward={
- vec=function() return Vec3(dir.x,0,dir.z) end,
- at=function(at,dir)
- at.x=at.x+dir.x
- at.z=at.z+dir.z
- end,
- attack=turtle.attack,
- raw_name="forward",suck=turtle.suck, dig=turtle.dig, raw_move=turtle.forward, detect=turtle.detect, place=turtle.place,compare=turtle.compare, drop=turtle.drop,},
- down={
- vec=function() return Vec3(0,-1,0) end,
- at=function(at)
- at.y=at.y-1
- end,
- attack=turtle.attackDown,
- raw_name="down",suck=turtle.suckDown,dig=turtle.digDown, raw_move=turtle.down, detect=turtle.detectDown, place=turtle.placeDown,compare=turtle.compareDown, drop=turtle.dropDown,},
- back={
- vec=function() return Vec3(-dir.x,0,-dir.z) end,
- at=function(at,dir)
- at.x=at.x-dir.x
- at.z=at.z-dir.z
- end,
- raw_name='back',raw_move=turtle.back},
- }
- local t_dirs={
- left={
- vec=function() return Vec3(dir.z,0,-dir.x) end,
- raw_name='turnLeft',raw_move=turtle.turnLeft,at=function(at,dir) dir.x, dir.z = dir.z, -dir.x end },
- right={
- vec=function() return Vec3(-dir.z,0,dir.x) end,
- raw_name='turnRight',raw_move=turtle.turnRight,at=function(at,dir) dir.x, dir.z = -dir.z, dir.x end},
- }
- local opp={
- up='down',
- forward='back',
- right='left',
- }
- for i,v in pairs(opp) do opp[v]=i end
- local function helpMove(doF,moveF,undoF,fuel)
- if fuel then ensureFuel() end
- doF(pos,dir)
- save()
- local r=moveF()
- if not r then undoF(pos,dir) save() end
- return r
- end
- for mv,t in pairs(s_dirs) do
- local args={t.at,t.raw_move,s_dirs[opp[mv]].at,true}
- turtle[t.raw_name]=function() return helpMove(unpack(args)) end
- t.move=turtle[t.raw_name]
- moves[mv]=t
- end
- for mv,t in pairs(t_dirs) do
- local args={t.at,t.raw_move,t_dirs[opp[mv]].at}
- turtle[t.raw_name]=function() return helpMove(unpack(args)) end
- t.move=turtle[t.raw_name]
- moves[mv]=t
- end
- --aliases
- -- local function tdo(turnF,doF) return function(...) turnF() return doF(...) end end
- -- moves.around={}
- -- util.shcopy({
- -- suck=tdo(moves.around,forward.suck),
- -- attack=tdo(moves.around,forward.attack),
- -- drop=tdo(moves.around,forward.drop),
- -- move=function() turtle.turnLeft() turtle.turnLeft() end,
- -- at=function(at,dir) moves.left.at(at,dir) moves.left.at(at,dir) end,
- -- },moves.around)
- for i,v in pairs(moves) do
- setmetatable(v,
- {__call=function(t,...)
- return v.move(...)
- end,
- })
- end
- moves.top=moves.up
- moves.bottom=moves.down
- moves.front=moves.forward
- end
- for i,v in pairs(moves) do
- local env=(getfenv())
- env[i]=v
- end
- ---
- --world pos related
- compass={
- {name='north',n=0,vec=function() return Vec3( 0, 0,-1) end},
- {name='east',n=1,vec=function() return Vec3( 1, 0, 0) end},
- {name='south',n=2,vec=function() return Vec3( 0, 0, 1) end},
- {name='west',n=3,vec=function() return Vec3(-1, 0, 0) end},
- }
- for i,v in pairs(compass) do compass[v.name]=v end
- --vec, mov, string, or nil
- function getFacing(mv)
- local vec
- if mv==nil then vec=dir
- elseif classof(mv)==Vec3 then vec=mv
- elseif type(mv)=='string' then vec=moves[mv].vec()
- else error() end
- for i,c in ipairs(compass) do
- if vec==c.vec() then return c.name end
- end
- end
- function getChunk(v)
- v=v or pos
- return math.floor(v.x/16), math.floor(v.z/16)
- end
- function getChunkPosition(v)
- v=v or pos
- return v.x%16, v.z%16
- end
- ---
- --[[input patterns:
- x,y,z ->vector
- string->move or compass
- move or compas->vec
- drop
- suck
- place
- ]]
- --arg transformer
- local function trf(...)
- if classof(...)==Vec3 then
- return ...
- elseif type(...)=='string' then
- if moves[...] then
- return moves[...].vec()
- elseif compass[...] then
- return compass[...].vec()
- end
- else return Vec3(...) end
- end
- --public api
- --mv based
- local lastMoveNeededDig=false
- function clear(mv)
- mv=type(mv)=='string' and moves[mv] or mv
- local maxCount=mode.count
- local delay=mode.delay
- local dig_count=0
- local count=0
- while (mv.detect()) and (count < maxCount) do
- if(count == 0) then
- -- Am about to dig a block - check that it is not a chest if necessary
- -- If we are looking for chests, then check that this isn't a chest before moving
- if not mode.dig then
- return false,'dig'
- end
- end
- dig_count = (mv.dig() and 1 or 0)+count
- count=count+1
- sleep(delay)
- end
- if dig_count~=0 then lastMoveNeededDig=true end
- return (count~=maxCount),count,dig_count
- end
- function force_back()
- if not moves.back() then
- if move'back' then
- turn'back'
- return true,true
- else
- return false
- end
- end
- return true
- end
- local function _force(mv,key)
- mv=type(mv)=='string' and moves[mv] or mv
- if mv==moves.back then return force_back(mv,key) end
- local suc
- if not lastMoveNeededDig then
- suc=mv[key]()
- if suc then return true,0,0 end
- end
- local maxCount=mode.count
- local delay=mode.delay
- local dig_count=0
- local count=0
- while (not suc) and (count < maxCount) do
- if mv.detect() then
- if(count == 0) then
- -- Am about to dig a block - check that it is not a chest if necessary
- -- If we are looking for chests, then check that this isn't a chest before moving
- if not mode.dig then
- return false,'dig'
- end
- end
- dig_count = (mv.dig() and 1 or 0)+count
- else
- if not mode.attack then
- return false,'attack'
- end
- mv.attack()
- end
- sleep(delay)
- suc=mv[key]()
- count=count+1
- end
- if dig_count~=0 then lastMoveNeededDig=true end
- return suc,count,dig_count
- end
- function force(mv)
- return _force(mv,'move')
- end
- function place(mv)
- return _force(mv,'place')
- end
- function advance(mv, steps)
- mv=type(mv)=='string' and moves[mv] or mv
- steps = steps or 1
- for step = 1,steps do
- force(mv)
- end
- end
- --vector based
- function turn(...)
- local to=trf(...)
- local x,z = sign(to.x), sign(to.z)
- if x == dir.x and z == dir.z then
- return
- elseif (x == -dir.x and x~=0) or (z == -dir.z and z~=0) then --or?
- left()
- left()
- elseif x ~= 0 then
- if x == -dir.z then right() else left() end
- elseif z ~= 0 then
- if z == dir.x then right() else left() end
- end
- end
- function goto(...)
- local to=trf(...)
- if to.y>pos.y then
- advance(up, to.y-pos.y)
- elseif to.y<pos.y then
- advance(down, pos.y-to.y)
- end
- if to.x ~= pos.x then
- turn({x=to.x-pos.x})
- advance(forward, math.abs(to.x-pos.x))
- end
- if to.z ~= pos.z then
- turn({z=to.z-pos.z})
- advance(forward, math.abs(to.z-pos.z))
- end
- end
- function move(...)
- local to=trf(...)
- return goto(to+pos)
- end
- ---
- function debug()
- return ({lastMoveNeededDig=lastMoveNeededDig,pos_dir={get()}})
- end
- --init code
- local function update()
- load()
- pos,dir=(pos or Vec3()),(dir or compass.south.vec())
- save()
- if mode.gps then
- local ok, pos_,dir_=pcall(getGps)
- if ok then set(pos_,dir_)
- else log('WARNING','Could not use gps:',pos_) end
- end
- end
- function getGps()
- rednet.open'right'
- local x,y,z=gps.locate(5)
- if x then
- gps_pos=Vec3(x,y,z)
- local org=(get())
- for _,c in ipairs(compass) do
- if goto(c.vec()+org) then
- local x,y,z=gps.locate(5)
- if x then
- local gps_pos2=Vec3(x,y,z)
- local dir=gps_pos2-gps_pos
- if dir~=Vec3(0,0,0) then
- return true,gps_pos2,dir
- end
- end
- end
- end
- error'blocked'
- else
- error'no gps signal'
- end
- end
- ---
- update()
- api={
- debug=debug,
- set=set,
- get=get,
- moves=moves,
- front=moves.forward,
- bottom=moves.down,
- top=moves.up,
- clear=clear,
- force=force,
- place=place,
- advance=advance,
- mode=mode,
- getFacing=getFacing,
- getChunk=getChunk,
- getChunkPos=getChunkPos,
- compass=compass,
- getGps=getGps,
- turn=turn,
- move=move,
- goto=goto,
- }
- for i,v in pairs(moves) do api[i]=v end
- api.api=api
- return api]===================],"CoolisOS/bin/utils/Vec3.lua",[===================[--[[
- Advanced Vector API by Tomass1996
- Vector 'Object' Functions And Fields:
- vector.x -- Vectors X component
- vector.y -- Vectors Y component
- vector.z -- Vectors Z component
- vector:add(otherVector) -- Component-wise addition
- vector:scalarAdd(n) -- Scalar addition
- vector:subtract(otherVector) -- Component-wise subtraction
- vector:scalarSubtract(n) -- Scalar subtraction
- vector:eldot(otherVector) -- Component-wise multiplication
- vector:scalarMultiply(n) -- Scalar multiplication
- vector:divide(otherVector) -- Component-wise division
- vector:scalarDivide(n) -- Scalar division
- vector:length() -- Get the length of the vector
- vector:lengthSq() -- Get the length ^ 2 of the vector
- vector:distance(otherVector) -- Get the distance away from a vector
- vector:distanceSq(otherVector) -- Get the distance away from a vector, squared
- vector:normalize() -- Get the normalized vector
- vector:dot(otherVector) -- Get the dot product of vector and otherVector
- vector:cross(otherVector) -- Get the cross product of vector and otherVector
- vector:containedWithin(minVec, maxVec)-- Check to see if vector is contained within minVec and maxVec
- vector:clampX(min, max) -- Clamp the X component
- vector:clampY(min, max) -- Clamp the Y component
- vector:clampZ(min, max) -- Clamp the Z component
- vector:floor() -- Rounds all components down
- vector:ceil() -- Rounds all components up
- vector:round() -- Rounds all components to the closest integer
- vector:absolute() -- Vector with absolute values of components
- vector:isCollinearWith(otherVector) -- Checks to see if vector is collinear with otherVector
- vector:getIntermediateWithX(other, x) -- New vector with given x value along the line between vector and other, or nil if not possible
- vector:getIntermediateWithY(other, y) -- New vector with given y value along the line between vector and other, or nil if not possible
- vector:getIntermediateWithZ(other, z) -- New vector with given z value along the line between vector and other, or nil if not possible
- vector:rotateAroundX(angle) -- Rotates vector around the x axis by the specified angle(radians)
- vector:rotateAroundY(angle) -- Rotates vector around the y axis by the specified angle(radians)
- vector:rotateAroundZ(angle) -- Rotates vector around the z axis by the specified angle(radians)
- vector:clone() -- Returns a new vector with same component values as vector
- vector:equals(otherVector) -- Checks to see if vector and otherVector are equal
- vector:tostring() -- Returns the string representation of vector "(x, y, z)"
- Vector 'Object' Metatable Overrides: -- [x, y, z] represents a vector object in these examples, not irl
- To String -- tostring will get the string representation
- ie. tostring([1, 2, 3]) --> "(1, 2, 3)"
- Unary Minus -- Using unary minus on a vector will result in the negative of vector
- ie. -[1, -2, 3] --> [-1, 2, -3]
- Addition -- Can add two vectors or vector and number with +
- ie. [1, 2, 3] + [4, 5, 6] --> [5, 7, 9]
- [1, 2, 3] + 3 --> [4, 5, 6]
- Subtraction -- Can subtract two vectors or vector and number with -
- ie. [4, 5, 6] - [1, 2, 3] --> [3, 3, 3]
- [4, 5, 6] - 3 --> [1, 2, 3]
- Multiplication -- Can dot two vectors or vector and number with *
- ie. [1, 2, 3] * [4, 5, 6] --> [4, 10, 18]
- [1, 2, 3] * 3 --> [3, 6, 9]
- Division -- Can divide two vectors or vector and number with /
- ie. [4, 10, 18] / [1, 2, 3] --> [4, 5, 6]
- [3, 6, 9] / 3 --> [1, 2, 3]
- Equality -- Can check if two vectors are the same with ==
- ie. [4, 5, 6] == [4, 5, 6] --> true
- [4, 5, 6] == [4, 99, 6] --> false
- Vector API functions:
- Vector.getMinimum(v1, v2) -- Gets the minimum components of two vectors
- Vector.getMaximum(v1, v2) -- Gets the maximum components of two vectors
- Vector.getMidpoint(v1, v2) -- Gets the midpoint of two vectors
- Vector.isVector(v) -- Checks whether v is a vector created by this api
- Vector(x, y, z) -- Creates a new vector object with the component values
- --]]
- include'kernel.class'
- class.Vec3()
- function Vec3:__init(t,y,z)
- if type(t)=='table' then
- self.x = t.x or t[1] or 0
- self.y = t.y or t[2] or 0
- self.z = t.z or t[3] or 0
- else
- self.x = t or 0
- self.y = y or 0
- self.z = z or 0
- end
- end
- local vector=Vec3
- local new=Vec3
- local isVector=function(v) return classof(v)==Vec3 end
- local getType=ofType
- local vector = {
- add = function(self, v)
- self.x=self.x + v.x
- self.y=self.y + v.y
- self.z=self.z + v.z
- return self
- end,
- scalarAdd = function(self, n)
- self.x=self.x + n
- self.y=self.y + n
- self.z=self.z + n
- return self
- end,
- subtract = function(self, v)
- self.x=self.x - v.x
- self.y=self.y - v.y
- self.z=self.z - v.z
- return self
- end,
- scalarSubtract = function(self, n)
- return self:scalarAdd(-n)
- end,
- eldot = function(self, v)
- self.x=self.x * v.x
- self.y=self.y * v.y
- self.z=self.z * v.z
- return self
- end,
- scalarMultiply = function(self, n)
- self.x=self.x * n
- self.y=self.y * n
- self.z=self.z * n
- return self
- end,
- divide = function(self, o)
- return new(
- self.x / o.x,
- self.y / o.y,
- self.z / o.z
- )
- end,
- scalarDivide = function(self, n)
- return new(
- self.x / n,
- self.y / n,
- self.z / n
- )
- end,
- length = function(self)
- return math.sqrt(
- self.x * self.x
- + self.y * self.y
- + self.z * self.z
- )
- end,
- lengthSq = function(self)
- return (
- self.x * self.x
- + self.y * self.y
- + self.z * self.z
- )
- end,
- distance = function(self, o)
- return math.sqrt(
- math.pow(o.x - self.x, 2)
- + math.pow(o.y - self.y, 2)
- + math.pow(o.z - self.z, 2)
- )
- end,
- distanceSq = function(self, o)
- return (
- math.pow(o.x - self.x, 2)
- + math.pow(o.y - self.y, 2)
- + math.pow(o.z - self.z, 2)
- )
- end,
- normalize = function(self)
- return self:scalarDivide(self:length())
- end,
- dot = function(self, o)
- return (
- self.x * o.x
- + self.y * o.y
- + self.z * o.z
- )
- end,
- cross = function(self, o)
- return new(
- self.y * o.z - self.z * o.y,
- self.z * o.x - self.x * o.z,
- self.x * o.y - self.y * o.x
- )
- end,
- containedWithin = function(self, min, max)
- return (
- self.x >= min.x and self.x <= max.x
- and self.y >= min.y and self.y <= max.y
- and self.z >= min.z and self.z <= max.z
- )
- end,
- clampX = function(self, min, max)
- return new(
- math.max(min, math.min(max, self.x)),
- self.y,
- self.z
- )
- end,
- clampY = function(self, min, max)
- return new(
- self.x,
- math.max(min, math.min(max, self.y)),
- self.z
- )
- end,
- clampZ = function(self, min, max)
- return new(
- self.x,
- self.y,
- math.max(min, math.min(max, self.z))
- )
- end,
- floor = function(self)
- return new(
- math.floor(self.x),
- math.floor(self.y),
- math.floor(self.z)
- )
- end,
- ceil = function(self)
- return new(
- math.ceil(self.x),
- math.ceil(self.y),
- math.ceil(self.z)
- )
- end,
- round = function(self)
- return new(
- math.floor(self.x + 0.5),
- math.floor(self.y + 0.5),
- math.floor(self.z + 0.5)
- )
- end,
- absolute = function(self)
- return new(
- math.abs(self.x),
- math.abs(self.y),
- math.abs(self.z)
- )
- end,
- isCollinearWith = function(self, o)
- if self.x == 0 and self.y == 0 and self.z == 0 then
- return true
- end
- local otherX, otherY, otherZ = o.x, o.y, o.z
- if otherX == 0 and otherY == 0 and otherZ == 0 then
- return true
- end
- if (self.x == 0) ~= (otherX == 0) then return false end
- if (self.y == 0) ~= (otherY == 0) then return false end
- if (self.z == 0) ~= (otherZ == 0) then return false end
- local quotientX = otherX / self.x
- if quotientX == quotientX then
- return o:equals(self:scalarMultiply(quotientX))
- end
- local quotientY = otherY / self.y
- if quotientY == quotientY then
- return o:equals(self:scalarMultiply(quotientY))
- end
- local quotientZ = otherZ / self.z
- if quotientZ == quotientZ then
- return o:equals(self:scalarMultiply(quotientZ))
- end
- end,
- getIntermediateWithX = function(self, o, v)
- local vX = o.x - self.x
- local vY = o.y - self.y
- local vZ = o.z - self.z
- if vX * vX < 1.0000000116860974e-7 then
- return nil
- else
- local nMul = (v - self.x) / vX
- return (
- (nMul >= 0 and nMul <= 1)
- and new(
- self.x + vX * nMul,
- self.y + vY * nMul,
- self.z + vZ * nMul
- )
- or nil
- )
- end
- end,
- getIntermediateWithY = function(self, o, v)
- local vX = o.x - self.x
- local vY = o.y - self.y
- local vZ = o.z - self.z
- if vY * vY < 1.0000000116860974e-7 then
- return nil
- else
- local nMul = (v - self.y) / vY
- return (
- (nMul >= 0 and nMul <= 1)
- and new(
- self.x + vX * nMul,
- self.y + vY * nMul,
- self.z + vZ * nMul
- )
- or nil
- )
- end
- end,
- getIntermediateWithZ = function(self, o, v)
- local vX = o.x - self.x
- local vY = o.y - self.y
- local vZ = o.z - self.z
- if vZ * vZ < 1.0000000116860974e-7 then
- return nil
- else
- local nMul = (v - self.z) / vZ
- return (
- (nMul >= 0 and nMul <= 1)
- and new(
- self.x + vX * nMul,
- self.y + vY * nMul,
- self.z + vZ * nMul
- )
- or nil
- )
- end
- end,
- rotateAroundX = function(self, n)
- local c, s = math.cos(n), math.sin(n)
- return new(
- self.x,
- self.y * c + self.z * s,
- self.z * c - self.y * s
- )
- end,
- rotateAroundY = function(self, n)
- local c, s = math.cos(n), math.sin(n)
- return new(
- self.x * c + self.z * s,
- self.y,
- self.z * c - self.x * s
- )
- end,
- rotateAroundZ = function(self, n)
- local c, s = math.cos(n), math.sin(n)
- return new(
- self.x * c + self.y * s,
- self.y * c - self.x * s,
- self.z
- )
- end,
- clone = function(self)
- return Vec3(self)
- end,
- equals = function(self, o)
- if not isVector(self) or not isVector(o) then return false end
- return (
- o.x == self.x
- and o.y == self.y
- and o.z == self.z
- )
- end,
- tostring = function(self)
- return "("..self.x..", "..self.y..", "..self.z..")"
- end,
- getMinimum=function(v1, v2)
- return new(
- math.min(v1.x, v2.x),
- math.min(v1.y, v2.y),
- math.min(v1.z, v2.z)
- )
- end,
- getMidpoint= function(v1, v2)
- return new(
- (v1.x + v2.x) / 2,
- (v1.y + v2.y) / 2,
- (v1.z + v2.z) / 2
- )
- end,
- __serialize=function(self)
- return {self.x,self.y,self.z}
- end
- }
- local vmetatable = {
- __tostring = vector.tostring,
- __unm = function(v) return v:scalarMultiply(-1) end,
- __add = function(a, b)
- if type(b) == "number" and isVector(a) then
- return a:clone():scalarAdd(b)
- elseif type(a) == "number" and isVector(b) then
- return b:clone():scalarAdd(a)
- elseif isVector(a) and isVector(b) then
- return a:clone():add(b)
- else
- error("Attempt to perform vector addition on <"..getType(a).."> and <"..getType(b)..">")
- end
- end,
- __sub = function(a, b)
- if type(b) == "number" and isVector(a) then
- return a:clone():scalarSubtract(b)
- elseif type(a) == "number" and isVector(b) then
- return b:clone():scalarSubtract(a)
- elseif isVector(a) and isVector(b) then
- return a:clone():subtract(b)
- else
- error("Attempt to perform vector subtraction on <"..getType(a).."> and <"..getType(b)..">")
- end
- end,
- __mul = function(a, b)
- if type(b) == "number" and isVector(a) then
- return a:scalarMultiply(b)
- elseif type(a) == "number" and isVector(b) then
- return b:scalarMultiply(a)
- elseif isVector(a) and isVector(b) then
- return a:dot(b)
- else
- error("Attempt to perform vector multiplication on <"..getType(a).."> and <"..getType(b)..">")
- end
- end,
- __div = function(a, b)
- if type(b) == "number" and isVector(a) then
- return a:scalarDivide(b)
- elseif type(a) == "number" and isVector(b) then
- return b:scalarDivide(a)
- elseif isVector(a) and isVector(b) then
- return a:divide(b)
- else
- error("Attempt to perform vector division on <"..getType(a).."> and <"..getType(b)..">")
- end
- end,
- __eq = vector.equals,
- __pow = vector.cross,
- }
- util.shcopy(vector,Vec3)
- util.shcopy(vmetatable,Vec3)
- --[[test
- v=require'bin.utils.Vec3'
- ]]
- return Vec3]===================],"CoolisOS/bin/utils/pl",true,"CoolisOS/bin/utils/pl/List.lua",[===================[--- Python-style list class.
- --
- -- **Please Note**: methods that change the list will return the list.
- -- This is to allow for method chaining, but please note that `ls = ls:sort()`
- -- does not mean that a new copy of the list is made. In-place (mutable) methods
- -- are marked as returning 'the list' in this documentation.
- --
- -- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion}
- --
- -- See <a href="http://www.python.org/doc/current/tut/tut.html">http://www.python.org/doc/current/tut/tut.html</a>, section 5.1
- --
- -- **Note**: The comments before some of the functions are from the Python docs
- -- and contain Python code.
- --
- -- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan.
- --
- -- Dependencies: `pl.utils`, `pl.tablex`
- -- @module pl.List
- -- @pragma nostrip
- local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort
- local setmetatable, getmetatable,type,tostring,assert,string,next = setmetatable,getmetatable,type,tostring,assert,string,next
- local write = io.write
- local tablex = require 'tablex'
- local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues
- local tablex = tablex
- local tsub = tablex.sub
- local utils = require 'utils'
- local array_tostring,split,is_type,assert_arg,function_arg = utils.array_tostring,utils.split,utils.is_type,utils.assert_arg,utils.function_arg
- local normalize_slice = tablex._normalize_slice
- --[[
- module ('pl.List',utils._module)
- ]]
- local Multimap = utils.stdmt.MultiMap
- -- metatable for our list objects
- local List = utils.stdmt.List
- List.__index = List
- List._class = List
- local iter
- -- we give the metatable its own metatable so that we can call it like a function!
- setmetatable(List,{
- __call = function (tbl,arg)
- return List.new(arg)
- end,
- })
- local function makelist (t,obj)
- local klass = List
- if obj then
- klass = getmetatable(obj)
- end
- return setmetatable(t,klass)
- end
- local function is_list(t)
- return getmetatable(t) == List
- end
- local function simple_table(t)
- return type(t) == 'table' and not is_list(t) and #t > 0
- end
- function List:_init (src)
- if src then
- for v in iter(src) do
- tinsert(self,v)
- end
- end
- end
- --- Create a new list. Can optionally pass a table;
- -- passing another instance of List will cause a copy to be created
- -- we pass anything which isn't a simple table to iterate() to work out
- -- an appropriate iterator @see List.iterate
- -- @param t An optional list-like table
- -- @return a new List
- -- @usage ls = List(); ls = List {1,2,3,4}
- function List.new(t)
- local ls
- if not simple_table(t) then
- ls = {}
- List._init(ls,t)
- else
- ls = t
- end
- makelist(ls)
- return ls
- end
- function List:clone()
- local ls = makelist({},self)
- List._init(ls,self)
- return ls
- end
- function List.default_map_with(T)
- return function(self,name)
- local f = T[name]
- if f then
- return function(self,...)
- return self:map(f,...)
- end
- else
- error("method not found: "..name,2)
- end
- end
- end
- ---Add an item to the end of the list.
- -- @param i An item
- -- @return the list
- function List:append(i)
- tinsert(self,i)
- return self
- end
- List.push = tinsert
- --- Extend the list by appending all the items in the given list.
- -- equivalent to 'a[len(a):] = L'.
- -- @param L Another List
- -- @return the list
- function List:extend(L)
- assert_arg(1,L,'table')
- for i = 1,#L do tinsert(self,L[i]) end
- return self
- end
- --- Insert an item at a given position. i is the index of the
- -- element before which to insert.
- -- @param i index of element before whichh to insert
- -- @param x A data item
- -- @return the list
- function List:insert(i, x)
- assert_arg(1,i,'number')
- tinsert(self,i,x)
- return self
- end
- --- Insert an item at the begining of the list.
- -- @param x a data item
- -- @return the list
- function List:put (x)
- return self:insert(1,x)
- end
- --- Remove an element given its index.
- -- (equivalent of Python's del s[i])
- -- @param i the index
- -- @return the list
- function List:remove (i)
- assert_arg(1,i,'number')
- tremove(self,i)
- return self
- end
- --- Remove the first item from the list whose value is given.
- -- (This is called 'remove' in Python; renamed to avoid confusion
- -- with table.remove)
- -- Return nil if there is no such item.
- -- @param x A data value
- -- @return the list
- function List:remove_value(x)
- for i=1,#self do
- if self[i]==x then tremove(self,i) return self end
- end
- return self
- end
- --- Remove the item at the given position in the list, and return it.
- -- If no index is specified, a:pop() returns the last item in the list.
- -- The item is also removed from the list.
- -- @param i An index
- -- @return the item
- function List:pop(i)
- if not i then i = #self end
- assert_arg(1,i,'number')
- return tremove(self,i)
- end
- List.get = List.pop
- --- Return the index in the list of the first item whose value is given.
- -- Return nil if there is no such item.
- -- @class function
- -- @name List:index
- -- @param x A data value
- -- @param idx where to start search (default 1)
- -- @return the index, or nil if not found.
- local tfind = tablex.find
- List.index = tfind
- --- does this list contain the value?.
- -- @param x A data value
- -- @return true or false
- function List:contains(x)
- return tfind(self,x) and true or false
- end
- --- Return the number of times value appears in the list.
- -- @param x A data value
- -- @return number of times x appears
- function List:count(x)
- local cnt=0
- for i=1,#self do
- if self[i]==x then cnt=cnt+1 end
- end
- return cnt
- end
- --- Sort the items of the list, in place.
- -- @param cmp an optional comparison function, default '<'
- -- @return the list
- function List:sort(cmp)
- if cmp then cmp = function_arg(1,cmp) end
- tsort(self,cmp)
- return self
- end
- --- return a sorted copy of this list.
- -- @param cmp an optional comparison function, default '<'
- -- @return a new list
- function List:sorted(cmp)
- return List(self):sort(cmp)
- end
- --- Reverse the elements of the list, in place.
- -- @return the list
- function List:reverse()
- local t = self
- local n = #t
- local n2 = n/2
- for i = 1,n2 do
- local k = n-i+1
- t[i],t[k] = t[k],t[i]
- end
- return self
- end
- --- return the minimum and the maximum value of the list.
- -- @return minimum value
- -- @return maximum value
- function List:minmax()
- local vmin,vmax = 1e70,-1e70
- for i = 1,#self do
- local v = self[i]
- if v < vmin then vmin = v end
- if v > vmax then vmax = v end
- end
- return vmin,vmax
- end
- --- Emulate list slicing. like 'list[first:last]' in Python.
- -- If first or last are negative then they are relative to the end of the list
- -- eg. slice(-2) gives last 2 entries in a list, and
- -- slice(-4,-2) gives from -4th to -2nd
- -- @param first An index
- -- @param last An index
- -- @return a new List
- function List:slice(first,last)
- return tsub(self,first,last)
- end
- --- empty the list.
- -- @return the list
- function List:clear()
- for i=1,#self do tremove(self) end
- return self
- end
- local eps = 1.0e-10
- --- Emulate Python's range(x) function.
- -- Include it in List table for tidiness
- -- @param start A number
- -- @param finish A number greater than start; if absent,
- -- then start is 1 and finish is start
- -- @param incr an optional increment (may be less than 1)
- -- @return a List from start .. finish
- -- @usage List.range(0,3) == List{0,1,2,3}
- -- @usage List.range(4) = List{1,2,3,4}
- -- @usage List.range(5,1,-1) == List{5,4,3,2,1}
- function List.range(start,finish,incr)
- if not finish then
- finish = start
- start = 1
- end
- if incr then
- assert_arg(3,incr,'number')
- if not utils.is_integer(incr) then finish = finish + eps end
- else
- incr = 1
- end
- assert_arg(1,start,'number')
- assert_arg(2,finish,'number')
- local t = List.new()
- for i=start,finish,incr do tinsert(t,i) end
- return t
- end
- --- list:len() is the same as #list.
- function List:len()
- return #self
- end
- -- Extended operations --
- --- Remove a subrange of elements.
- -- equivalent to 'del s[i1:i2]' in Python.
- -- @param i1 start of range
- -- @param i2 end of range
- -- @return the list
- function List:chop(i1,i2)
- return tremovevalues(self,i1,i2)
- end
- --- Insert a sublist into a list
- -- equivalent to 's[idx:idx] = list' in Python
- -- @param idx index
- -- @param list list to insert
- -- @return the list
- -- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20})
- function List:splice(idx,list)
- assert_arg(1,idx,'number')
- idx = idx - 1
- local i = 1
- for v in iter(list) do
- tinsert(self,i+idx,v)
- i = i + 1
- end
- return self
- end
- --- general slice assignment s[i1:i2] = seq.
- -- @param i1 start index
- -- @param i2 end index
- -- @param seq a list
- -- @return the list
- function List:slice_assign(i1,i2,seq)
- assert_arg(1,i1,'number')
- assert_arg(1,i2,'number')
- i1,i2 = normalize_slice(self,i1,i2)
- if i2 >= i1 then self:chop(i1,i2) end
- self:splice(i1,seq)
- return self
- end
- --- concatenation operator.
- -- @param L another List
- -- @return a new list consisting of the list with the elements of the new list appended
- function List:__concat(L)
- assert_arg(1,L,'table')
- local ls = self:clone()
- ls:extend(L)
- return ls
- end
- --- equality operator ==. True iff all elements of two lists are equal.
- -- @param L another List
- -- @return true or false
- function List:__eq(L)
- if #self ~= #L then return false end
- for i = 1,#self do
- if self[i] ~= L[i] then return false end
- end
- return true
- end
- --- join the elements of a list using a delimiter. <br>
- -- This method uses tostring on all elements.
- -- @param delim a delimiter string, can be empty.
- -- @return a string
- function List:join (delim)
- delim = delim or ''
- assert_arg(1,delim,'string')
- return concat(array_tostring(self),delim)
- end
- --- join a list of strings. <br>
- -- Uses table.concat directly.
- -- @class function
- -- @name List:concat
- -- @param delim a delimiter
- -- @return a string
- List.concat = concat
- local function tostring_q(val)
- local s = tostring(val)
- if type(val) == 'string' then
- s = '"'..s..'"'
- end
- return s
- end
- --- how our list should be rendered as a string. Uses join().
- -- @see List:join
- function List:__tostring()
- return '{'..self:join(',',tostring_q)..'}'
- end
- --[[
- -- NOTE: this works, but is unreliable. If you leave the loop before finishing,
- -- then the iterator is not reset.
- --- can iterate over a list directly.
- -- @usage for v in ls do print(v) end
- function List:__call()
- if not self.key then self.key = 1 end
- local value = self[self.key]
- self.key = self.key + 1
- if not value then self.key = nil end
- return value
- end
- --]]
- --[[
- function List.__call(t,v,i)
- i = (i or 0) + 1
- v = t[i]
- if v then return i, v end
- end
- --]]
- local MethodIter = {}
- function MethodIter:__index (name)
- return function(mm,...)
- return self.list:foreachm(name,...)
- end
- end
- --- call the function for each element of the list.
- -- @param fun a function or callable object
- -- @param ... optional values to pass to function
- function List:foreach (fun,...)
- if fun==nil then
- return setmetatable({list=self},MethodIter)
- end
- fun = function_arg(1,fun)
- for i = 1,#self do
- fun(self[i],...)
- end
- end
- function List:foreachm (name,...)
- for i = 1,#self do
- local obj = self[i]
- local f = assert(obj[name],"method not found on object")
- f(obj,...)
- end
- end
- --- create a list of all elements which match a function.
- -- @param fun a boolean function
- -- @param arg optional argument to be passed as second argument of the predicate
- -- @return a new filtered list.
- function List:filter (fun,arg)
- return makelist(filter(self,fun,arg),self)
- end
- --- split a string using a delimiter.
- -- @param s the string
- -- @param delim the delimiter (default spaces)
- -- @return a List of strings
- -- @see pl.utils.split
- function List.split (s,delim)
- assert_arg(1,s,'string')
- return makelist(split(s,delim))
- end
- local MethodMapper = {}
- function MethodMapper:__index (name)
- return function(mm,...)
- return self.list:mapm(name,...)
- end
- end
- --- apply a function to all elements.
- -- Any extra arguments will be passed to the function; if the function
- -- is `nil` then `map` returns a mapper object that maps over a method
- -- of the items
- -- @param fun a function of at least one argument
- -- @param ... arbitrary extra arguments.
- -- @return a new list: {f(x) for x in self}
- -- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'}
- -- @usage List{'one','two'}:map():sub(1,2) == {'on','tw'}
- -- @see pl.tablex.imap
- function List:map (fun,...)
- if fun==nil then
- return setmetatable({list=self},MethodMapper)
- end
- return makelist(imap(fun,self,...),self)
- end
- --- apply a function to all elements, in-place.
- -- Any extra arguments are passed to the function.
- -- @param fun A function that takes at least one argument
- -- @param ... arbitrary extra arguments.
- -- @return the list.
- function List:transform (fun,...)
- transform(fun,self,...)
- return self
- end
- --- apply a function to elements of two lists.
- -- Any extra arguments will be passed to the function
- -- @param fun a function of at least two arguments
- -- @param ls another list
- -- @param ... arbitrary extra arguments.
- -- @return a new list: {f(x,y) for x in self, for x in arg1}
- -- @see pl.tablex.imap2
- function List:map2 (fun,ls,...)
- return makelist(imap2(fun,self,ls,...),self)
- end
- --- apply a named method to all elements.
- -- Any extra arguments will be passed to the method.
- -- @param name name of method
- -- @param ... extra arguments
- -- @return a new list of the results
- -- @see pl.seq.mapmethod
- function List:mapm (name,...)
- local res = {}
- local t = self
- for i = 1,#t do
- local val = t[i]
- local fn = val[name]
- if not fn then error(type(val).." does not have method "..name,2) end
- res[i] = fn(val,...)
- end
- return makelist(res,self)
- end
- --- 'reduce' a list using a binary function.
- -- @param fun a function of two arguments
- -- @return result of the function
- -- @see pl.tablex.reduce
- function List:reduce (fun)
- return reduce(fun,self)
- end
- --- partition a list using a classifier function.
- -- The function may return nil, but this will be converted to the string key '<nil>'.
- -- @param fun a function of at least one argument
- -- @param ... will also be passed to the function
- -- @return a table where the keys are the returned values, and the values are Lists
- -- of values where the function returned that key. It is given the type of Multimap.
- -- @see pl.MultiMap
- function List:partition (fun,...)
- fun = function_arg(1,fun)
- local res = {}
- for i = 1,#self do
- local val = self[i]
- local klass = fun(val,...)
- if klass == nil then klass = '<nil>' end
- if not res[klass] then res[klass] = List() end
- res[klass]:append(val)
- end
- return setmetatable(res,Multimap)
- end
- --- return an iterator over all values.
- function List:iter ()
- return iter(self)
- end
- --- Create an iterator over a seqence.
- -- This captures the Python concept of 'sequence'.
- -- For tables, iterates over all values with integer indices.
- -- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function
- -- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55
- -- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p
- function List.iterate(seq)
- if type(seq) == 'string' then
- local idx = 0
- local n = #seq
- local sub = string.sub
- return function ()
- idx = idx + 1
- if idx > n then return nil
- else
- return sub(seq,idx,idx)
- end
- end
- elseif type(seq) == 'table' then
- local idx = 0
- local n = #seq
- return function()
- idx = idx + 1
- if idx > n then return nil
- else
- return seq[idx]
- end
- end
- elseif type(seq) == 'function' then
- return seq
- elseif type(seq) == 'userdata' and io.type(seq) == 'file' then
- return seq:lines()
- end
- end
- iter = List.iterate
- return List
- ]===================],"CoolisOS/bin/utils/pl/tablex.lua",[===================[--- Extended operations on Lua tables.
- --
- -- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide}
- --
- -- Dependencies: `pl.utils`
- -- @module pl.tablex
- local utils = require'utils'
- local getmetatable,setmetatable,require = getmetatable,setmetatable,require
- local append,remove = table.insert,table.remove
- local min,max = math.min,math.max
- local pairs,type,unpack,next,select,tostring = pairs,type,unpack,next,select,tostring
- local function_arg = utils.function_arg
- local Set = utils.stdmt.Set
- local List = utils.stdmt.List
- local Map = utils.stdmt.Map
- local assert_arg = utils.assert_arg
- local tablex = {}
- -- generally, functions that make copies of tables try to preserve the metatable.
- -- However, when the source has no obvious type, then we attach appropriate metatables
- -- like List, Map, etc to the result.
- local function setmeta (res,tbl,def)
- local mt = getmetatable(tbl) or def
- return setmetatable(res, mt)
- end
- local function makelist (res)
- return setmetatable(res,List)
- end
- local function check_meta (val)
- if type(val) == 'table' then return true end
- return getmetatable(val)
- end
- local function complain (idx,msg)
- error(('argument %d is not %s'):format(idx,msg),3)
- end
- local function assert_arg_indexable (idx,val)
- local mt = check_meta(val)
- if mt == true then return end
- if not(mt and mt.__len and mt.__index) then
- complain(idx,"indexable")
- end
- end
- local function assert_arg_iterable (idx,val)
- local mt = check_meta(val)
- if mt == true then return end
- if not(mt and mt.__pairs) then
- complain(idx,"iterable")
- end
- end
- local function assert_arg_writeable (idx,val)
- local mt = check_meta(val)
- if mt == true then return end
- if not(mt and mt.__newindex) then
- complain(idx,"writeable")
- end
- end
- --- copy a table into another, in-place.
- -- @param t1 destination table
- -- @param t2 source (any iterable object)
- -- @return first table
- function tablex.update (t1,t2)
- assert_arg_writeable(1,t1)
- assert_arg_iterable(2,t2)
- for k,v in pairs(t2) do
- t1[k] = v
- end
- return t1
- end
- --- total number of elements in this table.
- -- Note that this is distinct from `#t`, which is the number
- -- of values in the array part; this value will always
- -- be greater or equal. The difference gives the size of
- -- the hash part, for practical purposes. Works for any
- -- object with a __pairs metamethod.
- -- @param t a table
- -- @return the size
- function tablex.size (t)
- assert_arg_iterable(1,t)
- local i = 0
- for k in pairs(t) do i = i + 1 end
- return i
- end
- --- make a shallow copy of a table
- -- @param t an iterable source
- -- @return new table
- function tablex.copy (t)
- assert_arg_iterable(1,t)
- local res = {}
- for k,v in pairs(t) do
- res[k] = v
- end
- return res
- end
- --- make a deep copy of a table, recursively copying all the keys and fields.
- -- This will also set the copied table's metatable to that of the original.
- -- @param t A table
- -- @return new table
- function tablex.deepcopy(t)
- if type(t) ~= 'table' then return t end
- assert_arg_iterable(1,t)
- local mt = getmetatable(t)
- local res = {}
- for k,v in pairs(t) do
- if type(v) == 'table' then
- v = tablex.deepcopy(v)
- end
- res[k] = v
- end
- setmetatable(res,mt)
- return res
- end
- local abs, deepcompare = math.abs
- --- compare two values.
- -- if they are tables, then compare their keys and fields recursively.
- -- @param t1 A value
- -- @param t2 A value
- -- @param ignore_mt if true, ignore __eq metamethod (default false)
- -- @param eps if defined, then used for any number comparisons
- -- @return true or false
- function tablex.deepcompare(t1,t2,ignore_mt,eps)
- local ty1 = type(t1)
- local ty2 = type(t2)
- if ty1 ~= ty2 then return false end
- -- non-table types can be directly compared
- if ty1 ~= 'table' then
- if ty1 == 'number' and eps then return abs(t1-t2) < eps end
- return t1 == t2
- end
- -- as well as tables which have the metamethod __eq
- local mt = getmetatable(t1)
- if not ignore_mt and mt and mt.__eq then return t1 == t2 end
- for k1 in pairs(t1) do
- if t2[k1]==nil then return false end
- end
- for k2 in pairs(t2) do
- if t1[k2]==nil then return false end
- end
- for k1,v1 in pairs(t1) do
- local v2 = t2[k1]
- if not deepcompare(v1,v2,ignore_mt,eps) then return false end
- end
- return true
- end
- deepcompare = tablex.deepcompare
- --- compare two arrays using a predicate.
- -- @param t1 an array
- -- @param t2 an array
- -- @param cmp A comparison function
- function tablex.compare (t1,t2,cmp)
- assert_arg_indexable(1,t1)
- assert_arg_indexable(2,t2)
- if #t1 ~= #t2 then return false end
- cmp = function_arg(3,cmp)
- for k = 1,#t1 do
- if not cmp(t1[k],t2[k]) then return false end
- end
- return true
- end
- --- compare two list-like tables using an optional predicate, without regard for element order.
- -- @param t1 a list-like table
- -- @param t2 a list-like table
- -- @param cmp A comparison function (may be nil)
- function tablex.compare_no_order (t1,t2,cmp)
- assert_arg_indexable(1,t1)
- assert_arg_indexable(2,t2)
- if cmp then cmp = function_arg(3,cmp) end
- if #t1 ~= #t2 then return false end
- local visited = {}
- for i = 1,#t1 do
- local val = t1[i]
- local gotcha
- for j = 1,#t2 do if not visited[j] then
- local match
- if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end
- if match then
- gotcha = j
- break
- end
- end end
- if not gotcha then return false end
- visited[gotcha] = true
- end
- return true
- end
- --- return the index of a value in a list.
- -- Like string.find, there is an optional index to start searching,
- -- which can be negative.
- -- @param t A list-like table (i.e. with numerical indices)
- -- @param val A value
- -- @param idx index to start; -1 means last element,etc (default 1)
- -- @return index of value or nil if not found
- -- @usage find({10,20,30},20) == 2
- -- @usage find({'a','b','a','c'},'a',2) == 3
- function tablex.find(t,val,idx)
- assert_arg_indexable(1,t)
- idx = idx or 1
- if idx < 0 then idx = #t + idx + 1 end
- for i = idx,#t do
- if t[i] == val then return i end
- end
- return nil
- end
- --- return the index of a value in a list, searching from the end.
- -- Like string.find, there is an optional index to start searching,
- -- which can be negative.
- -- @param t A list-like table (i.e. with numerical indices)
- -- @param val A value
- -- @param idx index to start; -1 means last element,etc (default 1)
- -- @return index of value or nil if not found
- -- @usage rfind({10,10,10},10) == 3
- function tablex.rfind(t,val,idx)
- assert_arg_indexable(1,t)
- idx = idx or #t
- if idx < 0 then idx = #t + idx + 1 end
- for i = idx,1,-1 do
- if t[i] == val then return i end
- end
- return nil
- end
- --- return the index (or key) of a value in a table using a comparison function.
- -- @param t A table
- -- @param cmp A comparison function
- -- @param arg an optional second argument to the function
- -- @return index of value, or nil if not found
- -- @return value returned by comparison function
- function tablex.find_if(t,cmp,arg)
- assert_arg_iterable(1,t)
- cmp = function_arg(2,cmp)
- for k,v in pairs(t) do
- local c = cmp(v,arg)
- if c then return k,c end
- end
- return nil
- end
- --- return a list of all values in a table indexed by another list.
- -- @param tbl a table
- -- @param idx an index table (a list of keys)
- -- @return a list-like table
- -- @usage index_by({10,20,30,40},{2,4}) == {20,40}
- -- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3}
- function tablex.index_by(tbl,idx)
- assert_arg_indexable(1,tbl)
- assert_arg_indexable(2,idx)
- local res = {}
- for i = 1,#idx do
- res[i] = tbl[idx[i]]
- end
- return setmeta(res,tbl,List)
- end
- --- apply a function to all values of a table.
- -- This returns a table of the results.
- -- Any extra arguments are passed to the function.
- -- @param fun A function that takes at least one argument
- -- @param t A table
- -- @param ... optional arguments
- -- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4}
- function tablex.map(fun,t,...)
- assert_arg_iterable(1,t)
- fun = function_arg(1,fun)
- local res = {}
- for k,v in pairs(t) do
- res[k] = fun(v,...)
- end
- return setmeta(res,t)
- end
- --- apply a function to all values of a list.
- -- This returns a table of the results.
- -- Any extra arguments are passed to the function.
- -- @param fun A function that takes at least one argument
- -- @param t a table (applies to array part)
- -- @param ... optional arguments
- -- @return a list-like table
- -- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900}
- function tablex.imap(fun,t,...)
- assert_arg_indexable(1,t)
- fun = function_arg(1,fun)
- local res = {}
- for i = 1,#t do
- res[i] = fun(t[i],...) or false
- end
- return setmeta(res,t,List)
- end
- --- apply a named method to values from a table.
- -- @param name the method name
- -- @param t a list-like table
- -- @param ... any extra arguments to the method
- function tablex.map_named_method (name,t,...)
- utils.assert_string(1,name)
- assert_arg_indexable(2,t)
- local res = {}
- for i = 1,#t do
- local val = t[i]
- local fun = val[name]
- res[i] = fun(val,...)
- end
- return setmeta(res,t,List)
- end
- --- apply a function to all values of a table, in-place.
- -- Any extra arguments are passed to the function.
- -- @param fun A function that takes at least one argument
- -- @param t a table
- -- @param ... extra arguments
- function tablex.transform (fun,t,...)
- assert_arg_iterable(1,t)
- fun = function_arg(1,fun)
- for k,v in pairs(t) do
- t[k] = fun(v,...)
- end
- end
- --- generate a table of all numbers in a range
- -- @param start number
- -- @param finish number
- -- @param step optional increment (default 1 for increasing, -1 for decreasing)
- function tablex.range (start,finish,step)
- if start == finish then return {start}
- elseif start > finish then return {}
- end
- local res = {}
- local k = 1
- if not step then
- if finish > start then step = finish > start and 1 or -1 end
- end
- for i=start,finish,step do res[k]=i; k=k+1 end
- return res
- end
- --- apply a function to values from two tables.
- -- @param fun a function of at least two arguments
- -- @param t1 a table
- -- @param t2 a table
- -- @param ... extra arguments
- -- @return a table
- -- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44}
- function tablex.map2 (fun,t1,t2,...)
- assert_arg_iterable(1,t1)
- assert_arg_iterable(2,t2)
- fun = function_arg(1,fun)
- local res = {}
- for k,v in pairs(t1) do
- res[k] = fun(v,t2[k],...)
- end
- return setmeta(res,t1,List)
- end
- --- apply a function to values from two arrays.
- -- The result will be the length of the shortest array.
- -- @param fun a function of at least two arguments
- -- @param t1 a list-like table
- -- @param t2 a list-like table
- -- @param ... extra arguments
- -- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23}
- function tablex.imap2 (fun,t1,t2,...)
- assert_arg_indexable(2,t1)
- assert_arg_indexable(3,t2)
- fun = function_arg(1,fun)
- local res,n = {},math.min(#t1,#t2)
- for i = 1,n do
- res[i] = fun(t1[i],t2[i],...)
- end
- return res
- end
- --- 'reduce' a list using a binary function.
- -- @param fun a function of two arguments
- -- @param t a list-like table
- -- @return the result of the function
- -- @usage reduce('+',{1,2,3,4}) == 10
- function tablex.reduce (fun,t)
- assert_arg_indexable(2,t)
- fun = function_arg(1,fun)
- local n = #t
- local res = t[1]
- for i = 2,n do
- res = fun(res,t[i])
- end
- return res
- end
- --- apply a function to all elements of a table.
- -- The arguments to the function will be the value,
- -- the key and <i>finally</i> any extra arguments passed to this function.
- -- Note that the Lua 5.0 function table.foreach passed the <i>key</i> first.
- -- @param t a table
- -- @param fun a function with at least one argument
- -- @param ... extra arguments
- function tablex.foreach(t,fun,...)
- assert_arg_iterable(1,t)
- fun = function_arg(2,fun)
- for k,v in pairs(t) do
- fun(v,k,...)
- end
- end
- --- apply a function to all elements of a list-like table in order.
- -- The arguments to the function will be the value,
- -- the index and <i>finally</i> any extra arguments passed to this function
- -- @param t a table
- -- @param fun a function with at least one argument
- -- @param ... optional arguments
- function tablex.foreachi(t,fun,...)
- assert_arg_indexable(1,t)
- fun = function_arg(2,fun)
- for i = 1,#t do
- fun(t[i],i,...)
- end
- end
- --- Apply a function to a number of tables.
- -- A more general version of map
- -- The result is a table containing the result of applying that function to the
- -- ith value of each table. Length of output list is the minimum length of all the lists
- -- @param fun a function of n arguments
- -- @param ... n tables
- -- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333}
- -- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300}
- -- @param fun A function that takes as many arguments as there are tables
- function tablex.mapn(fun,...)
- fun = function_arg(1,fun)
- local res = {}
- local lists = {...}
- local minn = 1e40
- for i = 1,#lists do
- minn = min(minn,#(lists[i]))
- end
- for i = 1,minn do
- local args,k = {},1
- for j = 1,#lists do
- args[k] = lists[j][i]
- k = k + 1
- end
- res[#res+1] = fun(unpack(args))
- end
- return res
- end
- --- call the function with the key and value pairs from a table.
- -- The function can return a value and a key (note the order!). If both
- -- are not nil, then this pair is inserted into the result. If only value is not nil, then
- -- it is appended to the result.
- -- @param fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap.
- -- @param t A table
- -- @param ... optional arguments
- -- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10}
- -- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}}
- function tablex.pairmap(fun,t,...)
- assert_arg_iterable(1,t)
- fun = function_arg(1,fun)
- local res = {}
- for k,v in pairs(t) do
- local rv,rk = fun(k,v,...)
- if rk then
- res[rk] = rv
- else
- res[#res+1] = rv
- end
- end
- return res
- end
- local function keys_op(i,v) return i end
- --- return all the keys of a table in arbitrary order.
- -- @param t A table
- function tablex.keys(t)
- assert_arg_iterable(1,t)
- return makelist(tablex.pairmap(keys_op,t))
- end
- local function values_op(i,v) return v end
- --- return all the values of the table in arbitrary order
- -- @param t A table
- function tablex.values(t)
- assert_arg_iterable(1,t)
- return makelist(tablex.pairmap(values_op,t))
- end
- local function index_map_op (i,v) return i,v end
- --- create an index map from a list-like table. The original values become keys,
- -- and the associated values are the indices into the original list.
- -- @param t a list-like table
- -- @return a map-like table
- function tablex.index_map (t)
- assert_arg_indexable(1,t)
- return setmetatable(tablex.pairmap(index_map_op,t),Map)
- end
- local function set_op(i,v) return true,v end
- --- create a set from a list-like table. A set is a table where the original values
- -- become keys, and the associated values are all true.
- -- @param t a list-like table
- -- @return a set (a map-like table)
- function tablex.makeset (t)
- assert_arg_indexable(1,t)
- return setmetatable(tablex.pairmap(set_op,t),Set)
- end
- --- combine two tables, either as union or intersection. Corresponds to
- -- set operations for sets () but more general. Not particularly
- -- useful for list-like tables.
- -- @param t1 a table
- -- @param t2 a table
- -- @param dup true for a union, false for an intersection.
- -- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34}
- -- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23}
- -- @see tablex.index_map
- function tablex.merge (t1,t2,dup)
- assert_arg_iterable(1,t1)
- assert_arg_iterable(2,t2)
- local res = {}
- for k,v in pairs(t1) do
- if dup or t2[k] then res[k] = v end
- end
- if dup then
- for k,v in pairs(t2) do
- res[k] = v
- end
- end
- return setmeta(res,t1,Map)
- end
- --- a new table which is the difference of two tables.
- -- With sets (where the values are all true) this is set difference and
- -- symmetric difference depending on the third parameter.
- -- @param s1 a map-like table or set
- -- @param s2 a map-like table or set
- -- @param symm symmetric difference (default false)
- -- @return a map-like table or set
- function tablex.difference (s1,s2,symm)
- assert_arg_iterable(1,s1)
- assert_arg_iterable(2,s2)
- local res = {}
- for k,v in pairs(s1) do
- if not s2[k] then res[k] = v end
- end
- if symm then
- for k,v in pairs(s2) do
- if not s1[k] then res[k] = v end
- end
- end
- return setmeta(res,s1,Map)
- end
- --- A table where the key/values are the values and value counts of the table.
- -- @param t a list-like table
- -- @param cmp a function that defines equality (otherwise uses ==)
- -- @return a map-like table
- -- @see seq.count_map
- function tablex.count_map (t,cmp)
- assert_arg_indexable(1,t)
- local res,mask = {},{}
- cmp = function_arg(2,cmp)
- local n = #t
- for i = 1,#t do
- local v = t[i]
- if not mask[v] then
- mask[v] = true
- -- check this value against all other values
- res[v] = 1 -- there's at least one instance
- for j = i+1,n do
- local w = t[j]
- if cmp and cmp(v,w) or v == w then
- res[v] = res[v] + 1
- mask[w] = true
- end
- end
- end
- end
- return setmetatable(res,Map)
- end
- --- filter a table's values using a predicate function
- -- @param t a list-like table
- -- @param pred a boolean function
- -- @param arg optional argument to be passed as second argument of the predicate
- function tablex.filter (t,pred,arg)
- assert_arg_indexable(1,t)
- pred = function_arg(2,pred)
- local res,k = {},1
- for i = 1,#t do
- local v = t[i]
- if pred(v,arg) then
- res[k] = v
- k = k + 1
- end
- end
- return setmeta(res,t,List)
- end
- --- return a table where each element is a table of the ith values of an arbitrary
- -- number of tables. It is equivalent to a matrix transpose.
- -- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}}
- function tablex.zip(...)
- return tablex.mapn(function(...) return {...} end,...)
- end
- local _copy
- function _copy (dest,src,idest,isrc,nsrc,clean_tail)
- idest = idest or 1
- isrc = isrc or 1
- local iend
- if not nsrc then
- nsrc = #src
- iend = #src
- else
- iend = isrc + min(nsrc-1,#src-isrc)
- end
- if dest == src then -- special case
- if idest > isrc and iend >= idest then -- overlapping ranges
- src = tablex.sub(src,isrc,nsrc)
- isrc = 1; iend = #src
- end
- end
- for i = isrc,iend do
- dest[idest] = src[i]
- idest = idest + 1
- end
- if clean_tail then
- tablex.clear(dest,idest)
- end
- return dest
- end
- --- copy an array into another one, resizing the destination if necessary. <br>
- -- @param dest a list-like table
- -- @param src a list-like table
- -- @param idest where to start copying values from source (default 1)
- -- @param isrc where to start copying values into destination (default 1)
- -- @param nsrc number of elements to copy from source (default source size)
- function tablex.icopy (dest,src,idest,isrc,nsrc)
- assert_arg_indexable(1,dest)
- assert_arg_indexable(2,src)
- return _copy(dest,src,idest,isrc,nsrc,true)
- end
- --- copy an array into another one. <br>
- -- @param dest a list-like table
- -- @param src a list-like table
- -- @param idest where to start copying values from source (default 1)
- -- @param isrc where to start copying values into destination (default 1)
- -- @param nsrc number of elements to copy from source (default source size)
- function tablex.move (dest,src,idest,isrc,nsrc)
- assert_arg_indexable(1,dest)
- assert_arg_indexable(2,src)
- return _copy(dest,src,idest,isrc,nsrc,false)
- end
- function tablex._normalize_slice(self,first,last)
- local sz = #self
- if not first then first=1 end
- if first<0 then first=sz+first+1 end
- -- make the range _inclusive_!
- if not last then last=sz end
- if last < 0 then last=sz+1+last end
- return first,last
- end
- --- Extract a range from a table, like 'string.sub'.
- -- If first or last are negative then they are relative to the end of the list
- -- eg. sub(t,-2) gives last 2 entries in a list, and
- -- sub(t,-4,-2) gives from -4th to -2nd
- -- @param t a list-like table
- -- @param first An index
- -- @param last An index
- -- @return a new List
- function tablex.sub(t,first,last)
- assert_arg_indexable(1,t)
- first,last = tablex._normalize_slice(t,first,last)
- local res={}
- for i=first,last do append(res,t[i]) end
- return setmeta(res,t,List)
- end
- --- set an array range to a value. If it's a function we use the result
- -- of applying it to the indices.
- -- @param t a list-like table
- -- @param val a value
- -- @param i1 start range (default 1)
- -- @param i2 end range (default table size)
- function tablex.set (t,val,i1,i2)
- assert_arg_indexable(1,t)
- i1,i2 = i1 or 1,i2 or #t
- if utils.is_callable(val) then
- for i = i1,i2 do
- t[i] = val(i)
- end
- else
- for i = i1,i2 do
- t[i] = val
- end
- end
- end
- --- create a new array of specified size with initial value.
- -- @param n size
- -- @param val initial value (can be nil, but don't expect # to work!)
- -- @return the table
- function tablex.new (n,val)
- local res = {}
- tablex.set(res,val,1,n)
- return res
- end
- --- clear out the contents of a table.
- -- @param t a table
- -- @param istart optional start position
- function tablex.clear(t,istart)
- istart = istart or 1
- for i = istart,#t do remove(t) end
- end
- --- insert values into a table. <br>
- -- insertvalues(t, [pos,] values) <br>
- -- similar to table.insert but inserts values from given table "values",
- -- not the object itself, into table "t" at position "pos".
- function tablex.insertvalues(t, ...)
- assert_arg(1,t,'table')
- local pos, values
- if select('#', ...) == 1 then
- pos,values = #t+1, ...
- else
- pos,values = ...
- end
- if #values > 0 then
- for i=#t,pos,-1 do
- t[i+#values] = t[i]
- end
- local offset = 1 - pos
- for i=pos,pos+#values-1 do
- t[i] = values[i + offset]
- end
- end
- return t
- end
- --- remove a range of values from a table.
- -- @param t a list-like table
- -- @param i1 start index
- -- @param i2 end index
- -- @return the table
- function tablex.removevalues (t,i1,i2)
- assert_arg(1,t,'table')
- i1,i2 = tablex._normalize_slice(t,i1,i2)
- for i = i1,i2 do
- remove(t,i1)
- end
- return t
- end
- local _find
- _find = function (t,value,tables)
- for k,v in pairs(t) do
- if v == value then return k end
- end
- for k,v in pairs(t) do
- if not tables[v] and type(v) == 'table' then
- tables[v] = true
- local res = _find(v,value,tables)
- if res then
- res = tostring(res)
- if type(k) ~= 'string' then
- return '['..k..']'..res
- else
- return k..'.'..res
- end
- end
- end
- end
- end
- --- find a value in a table by recursive search.
- -- @param t the table
- -- @param value the value
- -- @param exclude any tables to avoid searching
- -- @usage search(_G,math.sin,{package.path}) == 'math.sin'
- -- @return a fieldspec, e.g. 'a.b' or 'math.sin'
- function tablex.search (t,value,exclude)
- assert_arg_iterable(1,t)
- local tables = {[t]=true}
- if exclude then
- for _,v in pairs(exclude) do tables[v] = true end
- end
- return _find(t,value,tables)
- end
- return tablex
- ]===================],"CoolisOS/bin/utils/pl/utils.lua",[===================[--- Generally useful routines.
- -- See @{01-introduction.md.Generally_useful_functions|the Guide}.
- -- @module pl.utils
- local format,gsub,byte = string.format,string.gsub,string.byte
- local clock = os.clock
- local stdout = io.stdout
- local append = table.insert
- local collisions = {}
- local utils = {}
- utils._VERSION = "1.1.0"
- local lua51 = rawget(_G,'setfenv')
- utils.lua51 = lua51
- if not lua51 then -- Lua 5.2 compatibility
- unpack = table.unpack
- loadstring = load
- end
- utils.dir_separator = '/'
- --- end this program gracefully.
- -- @param code The exit code or a message to be printed
- -- @param ... extra arguments for message's format'
- -- @see utils.fprintf
- function utils.quit(code,...)
- if type(code) == 'string' then
- utils.fprintf(io.stderr,code,...)
- code = -1
- else
- utils.fprintf(io.stderr,...)
- end
- io.stderr:write('\n')
- os.exit(code)
- end
- --- print an arbitrary number of arguments using a format.
- -- @param fmt The format (see string.format)
- -- @param ... Extra arguments for format
- function utils.printf(fmt,...)
- utils.assert_string(1,fmt)
- utils.fprintf(stdout,fmt,...)
- end
- --- write an arbitrary number of arguments to a file using a format.
- -- @param f File handle to write to.
- -- @param fmt The format (see string.format).
- -- @param ... Extra arguments for format
- function utils.fprintf(f,fmt,...)
- utils.assert_string(2,fmt)
- f:write(format(fmt,...))
- end
- local function import_symbol(T,k,v,libname)
- local key = rawget(T,k)
- -- warn about collisions!
- if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then
- utils.printf("warning: '%s.%s' overrides existing symbol\n",libname,k)
- end
- rawset(T,k,v)
- end
- local function lookup_lib(T,t)
- for k,v in pairs(T) do
- if v == t then return k end
- end
- return '?'
- end
- local already_imported = {}
- --- take a table and 'inject' it into the local namespace.
- -- @param t The Table
- -- @param T An optional destination table (defaults to callers environment)
- function utils.import(t,T)
- T = T or _G
- t = t or utils
- if type(t) == 'string' then
- t = require (t)
- end
- local libname = lookup_lib(T,t)
- if already_imported[t] then return end
- already_imported[t] = libname
- for k,v in pairs(t) do
- import_symbol(T,k,v,libname)
- end
- end
- utils.patterns = {
- FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*',
- INTEGER = '[+%-%d]%d*',
- IDEN = '[%a_][%w_]*',
- FILE = '[%a%.\\][:%][%w%._%-\\]*'
- }
- --- escape any 'magic' characters in a string
- -- @param s The input string
- function utils.escape(s)
- utils.assert_string(1,s)
- return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
- end
- --- return either of two values, depending on a condition.
- -- @param cond A condition
- -- @param value1 Value returned if cond is true
- -- @param value2 Value returned if cond is false (can be optional)
- function utils.choose(cond,value1,value2)
- if cond then return value1
- else return value2
- end
- end
- local raise
- --- return the contents of a file as a string
- -- @param filename The file path
- -- @param is_bin open in binary mode
- -- @return file contents
- function utils.readfile(filename,is_bin)
- local mode = is_bin and 'b' or ''
- utils.assert_string(1,filename)
- local f,err = io.open(filename,'r'..mode)
- if not f then return utils.raise (err) end
- local res,err = f:read('*a')
- f:close()
- if not res then return raise (err) end
- return res
- end
- --- write a string to a file
- -- @param filename The file path
- -- @param str The string
- -- @return true or nil
- -- @return error message
- -- @raise error if filename or str aren't strings
- function utils.writefile(filename,str)
- utils.assert_string(1,filename)
- utils.assert_string(2,str)
- local f,err = io.open(filename,'w')
- if not f then return raise(err) end
- f:write(str)
- f:close()
- return true
- end
- --- return the contents of a file as a list of lines
- -- @param filename The file path
- -- @return file contents as a table
- -- @raise errror if filename is not a string
- function utils.readlines(filename)
- utils.assert_string(1,filename)
- local f,err = io.open(filename,'r')
- if not f then return raise(err) end
- local res = {}
- for line in f:lines() do
- append(res,line)
- end
- f:close()
- return res
- end
- --- split a string into a list of strings separated by a delimiter.
- -- @param s The input string
- -- @param re A Lua string pattern; defaults to '%s+'
- -- @param plain don't use Lua patterns
- -- @param n optional maximum number of splits
- -- @return a list-like table
- -- @raise error if s is not a string
- function utils.split(s,re,plain,n)
- utils.assert_string(1,s)
- local find,sub,append = string.find, string.sub, table.insert
- local i1,ls = 1,{}
- if not re then re = '%s+' end
- if re == '' then return {s} end
- while true do
- local i2,i3 = find(s,re,i1,plain)
- if not i2 then
- local last = sub(s,i1)
- if last ~= '' then append(ls,last) end
- if #ls == 1 and ls[1] == '' then
- return {}
- else
- return ls
- end
- end
- append(ls,sub(s,i1,i2-1))
- if n and #ls == n then
- ls[#ls] = sub(s,i1)
- return ls
- end
- i1 = i3+1
- end
- end
- --- split a string into a number of values.
- -- @param s the string
- -- @param re the delimiter, default space
- -- @return n values
- -- @usage first,next = splitv('jane:doe',':')
- -- @see split
- function utils.splitv (s,re)
- return unpack(utils.split(s,re))
- end
- --- convert an array of values to strings.
- -- @param t a list-like table
- -- @param temp buffer to use, otherwise allocate
- -- @param tostr custom tostring function, called with (value,index).
- -- Otherwise use `tostring`
- -- @return the converted buffer
- function utils.array_tostring (t,temp,tostr)
- temp, tostr = temp or {}, tostr or tostring
- for i = 1,#t do
- temp[i] = tostr(t[i],i)
- end
- return temp
- end
- local lua51_load = load
- if utils.lua51 then -- define Lua 5.2 style load()
- function utils.load(str,src,mode,env)
- local chunk,err
- if type(str) == 'string' then
- chunk,err = loadstring(str,src)
- else
- chunk,err = lua51_load(str,src)
- end
- if chunk and env then setfenv(chunk,env) end
- return chunk,err
- end
- end
- --- execute a shell command.
- -- This is a compatibility function that returns the same for Lua 5.1 and Lua 5.2
- -- @param cmd a shell command
- -- @return true if successful
- -- @return actual return code
- function utils.execute (cmd)
- local res1,res2,res2 = os.execute(cmd)
- if lua51 then
- return res1==0,res1
- else
- return res1,res2
- end
- end
- if lua51 then
- function table.pack (...)
- local n = select('#',...)
- return {n=n; ...}
- end
- local sep = '/'
- function utils.searchpath (mod,path)
- mod = mod:gsub('%.',sep)
- for m in path:gmatch('[^;]+') do
- local nm = m:gsub('?',mod)
- local f = io.open(nm,'r')
- if f then f:close(); return nm end
- end
- end
- end
- if not table.pack then table.pack = _G.pack end
- if not rawget(_G,"pack") then _G.pack = table.pack end
- --- take an arbitrary set of arguments and make into a table.
- -- This returns the table and the size; works fine for nil arguments
- -- @param ... arguments
- -- @return table
- -- @return table size
- -- @usage local t,n = utils.args(...)
- --- 'memoize' a function (cache returned value for next call).
- -- This is useful if you have a function which is relatively expensive,
- -- but you don't know in advance what values will be required, so
- -- building a table upfront is wasteful/impossible.
- -- @param func a function of at least one argument
- -- @return a function with at least one argument, which is used as the key.
- function utils.memoize(func)
- return setmetatable({}, {
- __index = function(self, k, ...)
- local v = func(k,...)
- self[k] = v
- return v
- end,
- __call = function(self, k) return self[k] end
- })
- end
- --- is the object either a function or a callable object?.
- -- @param obj Object to check.
- function utils.is_callable (obj)
- return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call
- end
- --- is the object of the specified type?.
- -- If the type is a string, then use type, otherwise compare with metatable
- -- @param obj An object to check
- -- @param tp String of what type it should be
- function utils.is_type (obj,tp)
- if type(tp) == 'string' then return type(obj) == tp end
- local mt = getmetatable(obj)
- return tp == mt
- end
- local fileMT = getmetatable(io.stdout)
- --- a string representation of a type.
- -- For tables with metatables, we assume that the metatable has a `_name`
- -- field. Knows about Lua file objects.
- -- @param obj an object
- -- @return a string like 'number', 'table' or 'List'
- function utils.type (obj)
- local t = type(obj)
- if t == 'table' or t == 'userdata' then
- local mt = getmetatable(obj)
- if mt == fileMT then
- return 'file'
- else
- return mt._name or "unknown "..t
- end
- else
- return t
- end
- end
- --- is this number an integer?
- -- @param x a number
- -- @raise error if x is not a number
- function utils.is_integer (x)
- return math.ceil(x)==x
- end
- utils.stdmt = {
- List = {_name='List'}, Map = {_name='Map'},
- Set = {_name='Set'}, MultiMap = {_name='MultiMap'}
- }
- local _function_factories = {}
- --- associate a function factory with a type.
- -- A function factory takes an object of the given type and
- -- returns a function for evaluating it
- -- @param mt metatable
- -- @param fun a callable that returns a function
- function utils.add_function_factory (mt,fun)
- _function_factories[mt] = fun
- end
- local function _string_lambda(f)
- local raise = utils.raise
- if f:find '^|' or f:find '_' then
- local args,body = f:match '|([^|]*)|(.+)'
- if f:find '_' then
- args = '_'
- body = f
- else
- if not args then return raise 'bad string lambda' end
- end
- local fstr = 'return function('..args..') return '..body..' end'
- local fn,err = loadstring(fstr)
- if not fn then return raise(err) end
- fn = fn()
- return fn
- else return raise 'not a string lambda'
- end
- end
- --- an anonymous function as a string. This string is either of the form
- -- '|args| expression' or is a function of one argument, '_'
- -- @param lf function as a string
- -- @return a function
- -- @usage string_lambda '|x|x+1' (2) == 3
- -- @usage string_lambda '_+1 (2) == 3
- -- @function utils.string_lambda
- utils.string_lambda = utils.memoize(_string_lambda)
- local ops
- --- process a function argument.
- -- This is used throughout Penlight and defines what is meant by a function:
- -- Something that is callable, or an operator string as defined by <code>pl.operator</code>,
- -- such as '>' or '#'. If a function factory has been registered for the type, it will
- -- be called to get the function.
- -- @param idx argument index
- -- @param f a function, operator string, or callable object
- -- @param msg optional error message
- -- @return a callable
- -- @raise if idx is not a number or if f is not callable
- -- @see utils.is_callable
- function utils.function_arg (idx,f,msg)
- utils.assert_arg(1,idx,'number')
- local tp = type(f)
- if tp == 'function' then return f end -- no worries!
- -- ok, a string can correspond to an operator (like '==')
- if tp == 'string' then
- if not ops then ops = require 'pl.operator'.optable end
- local fn = ops[f]
- if fn then return fn end
- local fn, err = utils.string_lambda(f)
- if not fn then error(err..': '..f) end
- return fn
- elseif tp == 'table' or tp == 'userdata' then
- local mt = getmetatable(f)
- if not mt then error('not a callable object',2) end
- local ff = _function_factories[mt]
- if not ff then
- if not mt.__call then error('not a callable object',2) end
- return f
- else
- return ff(f) -- we have a function factory for this type!
- end
- end
- if not msg then msg = " must be callable" end
- if idx > 0 then
- error("argument "..idx..": "..msg,2)
- else
- error(msg,2)
- end
- end
- --- bind the first argument of the function to a value.
- -- @param fn a function of at least two values (may be an operator string)
- -- @param p a value
- -- @return a function such that f(x) is fn(p,x)
- -- @raise same as @{function_arg}
- -- @see pl.func.curry
- function utils.bind1 (fn,p)
- fn = utils.function_arg(1,fn)
- return function(...) return fn(p,...) end
- end
- --- bind the second argument of the function to a value.
- -- @param fn a function of at least two values (may be an operator string)
- -- @param p a value
- -- @return a function such that f(x) is fn(x,p)
- -- @raise same as @{function_arg}
- function utils.bind2 (fn,p)
- fn = utils.function_arg(1,fn)
- return function(x,...) return fn(x,p,...) end
- end
- --- assert that the given argument is in fact of the correct type.
- -- @param n argument index
- -- @param val the value
- -- @param tp the type
- -- @param verify an optional verfication function
- -- @param msg an optional custom message
- -- @param lev optional stack position for trace, default 2
- -- @raise if the argument n is not the correct type
- -- @usage assert_arg(1,t,'table')
- -- @usage assert_arg(n,val,'string',path.isdir,'not a directory')
- function utils.assert_arg (n,val,tp,verify,msg,lev)
- if type(val) ~= tp then
- error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2)
- end
- if verify and not verify(val) then
- error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2)
- end
- end
- --- assert the common case that the argument is a string.
- -- @param n argument index
- -- @param val a value that must be a string
- -- @raise val must be a string
- function utils.assert_string (n,val)
- utils.assert_arg(n,val,'string',nil,nil,3)
- end
- local err_mode = 'default'
- --- control the error strategy used by Penlight.
- -- Controls how <code>utils.raise</code> works; the default is for it
- -- to return nil and the error string, but if the mode is 'error' then
- -- it will throw an error. If mode is 'quit' it will immediately terminate
- -- the program.
- -- @param mode - either 'default', 'quit' or 'error'
- -- @see utils.raise
- function utils.on_error (mode)
- if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then
- err_mode = mode
- else
- -- fail loudly
- if err_mode == 'default' then err_mode = 'error' end
- utils.raise("Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'")
- end
- end
- --- used by Penlight functions to return errors. Its global behaviour is controlled
- -- by <code>utils.on_error</code>
- -- @param err the error string.
- -- @see utils.on_error
- function utils.raise (err)
- if err_mode == 'default' then return nil,err
- elseif err_mode == 'quit' then utils.quit(err)
- else error(err,2)
- end
- end
- raise = utils.raise
- --- load a code string or bytecode chunk.
- -- @param code Lua code as a string or bytecode
- -- @param name for source errors
- -- @param mode kind of chunk, 't' for text, 'b' for bytecode, 'bt' for all (default)
- -- @param env the environment for the new chunk (default nil)
- -- @return compiled chunk
- -- @return error message (chunk is nil)
- -- @function utils.load
- --- Lua 5.2 Compatible Functions
- -- @section lua52
- --- pack an argument list into a table.
- -- @param ... any arguments
- -- @return a table with field n set to the length
- -- @return the length
- -- @function table.pack
- ------
- -- return the full path where a Lua module name would be matched.
- -- @param mod module name, possibly dotted
- -- @param path a path in the same form as package.path or package.cpath
- -- @see path.package_path
- -- @function package.searchpath
- return utils
- ]===================],"CoolisOS/bin/utils/search",true,"CoolisOS/bin/utils/search/init.lua",[===================[---filesystem search API by CoolisTheName007
- local fs=fs
- local string=string
- local env=getfenv()
- setmetatable(env,nil)
- --The hard work here was writing the iterators to be efficient; for instance they don't use coroutines or recursion.
- ----DIRECTORY TREE-LIKE SEARCH
- ---iterator over files/dirs in directory dir and with maximum depth of search depth, default is infinite
- --following refer to the iterator returned, not the iterator created.
- --@treturn string path
- function iterTree(dir,depth)
- dir=dir or ''
- if dir=='/' then dir='' end
- local index={0}
- local dir_index={0}
- local ts={{dir,fs.list(dir),{}}}
- local level=1
- local t_dir
- return function()
- repeat
- index[level]=index[level]+1
- name=ts[level][2][index[level]]
- -- pprint(index,name)
- -- read()
- if name==nil then
- if (not ts[level][4]) and ts[level][3][1] then
- ts[level][4]=true
- ts[level][2],ts[level][3]=ts[level][3],ts[level][2]
- index[level]=0
- else
- level=level-1
- if level==0 then return end
- dir=ts[level][1]
- end
- else
- t_dir=ts[level][1]..'/'..name
- if fs.isDir(t_dir) then
- if ts[level][4] then
- if depth~=level then
- level=level+1
- dir=t_dir
- ts[level]={dir,fs.list(dir),{}}
- index[level]=0
- dir_index[level]=0
- end
- else
- dir_index[level]=dir_index[level]+1
- ts[level][3][dir_index[level]]=name
- break --send dir path
- end
- else
- break
- end
- end
- until false
- return dir..'/'..name
- end
- end
- ---iterator over files in directory dir and with maximum depth of search depth, default is infinite
- --following refer to the iterator returned, not the iterator created.
- --@treturn string path
- function iterFiles(dir,depth)
- local iter=iterTree(dir,depth)
- local path
- return function()
- repeat
- path=iter()
- if path then
- if not fs.isDir(path) then
- return path
- end
- else
- return
- end
- until false
- end
- end
- ---iterator over dirs in directory dir and with maximum depth of search depth, default is infinite
- --following refer to the iterator returned, not the iterator created.
- --@treturn string path
- function iterDir(dir,depth)
- local iter=iterTree(dir,depth)
- local path
- return function()
- repeat
- path=iter()
- if path then
- if fs.isDir(path) then
- return path
- end
- else
- return
- end
- until false
- end
- end
- ------GLOBS
- --WARNING: for compatibility with Lua ?, ? was replaced by # in globs; taken from https://github.com/davidm/lua-glob-pattern , by davidm
- --only needed for filename conversion, slashes are dealt with directly for iteration purposes.
- local function globtopattern(g)
- local p = "^" -- pattern being built
- local i = 0 -- index in g
- local c -- char at index i in g.
- -- unescape glob char
- local function unescape()
- if c == '\\' then
- i = i + 1; c = string.sub(g,i,i)
- if c == '' then
- p = '[^]'
- return false
- end
- end
- return true
- end
- -- escape pattern char
- local function escape(c)
- return c:match("^%w$") and c or '%' .. c
- end
- -- Convert tokens at end of charset.
- local function charset_end()
- while 1 do
- if c == '' then
- p = '[^]'
- return false
- elseif c == ']' then
- p = p .. ']'
- break
- else
- if not unescape() then break end
- local c1 = c
- i = i + 1; c = string.sub(g,i,i)
- if c == '' then
- p = '[^]'
- return false
- elseif c == '-' then
- i = i + 1; c = string.sub(g,i,i)
- if c == '' then
- p = '[^]'
- return false
- elseif c == ']' then
- p = p .. escape(c1) .. '%-]'
- break
- else
- if not unescape() then break end
- p = p .. escape(c1) .. '-' .. escape(c)
- end
- elseif c == ']' then
- p = p .. escape(c1) .. ']'
- break
- else
- p = p .. escape(c1)
- i = i - 1 -- put back
- end
- end
- i = i + 1; c = string.sub(g,i,i)
- end
- return true
- end
- -- Convert tokens in charset.
- local function charset()
- i = i + 1; c = string.sub(g,i,i)
- if c == '' or c == ']' then
- p = '[^]'
- return false
- elseif c == '^' or c == '!' then
- i = i + 1; c = string.sub(g,i,i)
- if c == ']' then
- -- ignored
- else
- p = p .. '[^'
- if not charset_end() then return false end
- end
- else
- p = p .. '['
- if not charset_end() then return false end
- end
- return true
- end
- --Convert tokens.
- while 1 do
- i = i + 1; c = string.sub(g,i,i)
- if c == '' then
- p = p .. '$'
- break
- elseif c == '#' then --?->#
- p = p .. '.'
- elseif c == '*' then
- p = p .. '.*'
- elseif c == '[' then
- if not charset() then break end
- elseif c == '\\' then
- i = i + 1; c = string.sub(g,i,i)
- if c == '' then
- p = p .. '\\$'
- break
- end
- p = p .. escape(c)
- else
- p = p .. escape(c)
- end
- end
- return p
- end
- ---turns a glob into a table structure proper for iterPatterns.
- local function compact(g)
- local nl={}
- local s1
- local n=0
- for c in string.gmatch(g,'[\\/]*([^/\\]+)[\\/]*') do
- if c:match('^[%w%s%.]*$') then
- s1=s1 and s1..'/'..c or c
- else
- n=n+1
- nl[n]={s1,globtopattern(c)}
- s1=nil
- end
- end
- if s1 then
- if n==0 then
- n=n+1
- nl[n]={s1}
- else
- nl[n][3]=s1
- end
- end
- return nl
- end
- ---iterator creator over valid paths defined by a table with the structure: {t1,...,tn}, where ti is:
- --for i<n: {dir,pat} - dir is the directory where to look for names matching the pattern pat
- --for i=n: {dir,pat,ending} -same but will combine the name (after successful match with pat) with the optional ending (can be nil) and check the resulting path
- --e.g., g={{'APIS','*'},{nil,'A'},{'B/C','#','aq/qwerty'}} will search in all subfolders of APIS for subfolders named A, and in each of those for a folder B
- --containing a folder C, and for all one-lettered folders in that folder for a folder aq containing a folder/file named qwerty.
- local function iterPatterns(l)
- local n=#l
- if n==0 then return function () return end end
- if n==1 and not l[1][2] and fs.exists(l[1][1]) then
- local done=false
- return function ()
- if not done then
- done=true
- return l[1][1]
- else
- return
- end
- end
- end
- local dir=l[1][1]
- local index={0}
- local ts
- ts={{dir,fs.isDir(dir) and fs.list(dir) or {}}}
- local level=1
- local t_dir
- local _
- return function()
- repeat
- index[level]=index[level]+1
- name=ts[level][2][index[level]]
- if name==nil then
- index[level]=nil
- level=level-1
- if level==0 then return end
- dir=ts[level][1]
- else
- if string.match(name,l[level][2]) then
- t_dir=dir..'/'..name
- if level==n then
- _=l[level][3]
- if _ then
- t_dir=t_dir..'/'.._
- if fs.exists(t_dir) then
- path=t_dir
- break
- end
- else
- path=t_dir
- break
- end
- elseif fs.isDir(t_dir) then
- level=level+1
- _=l[level][1]
- if _ then
- t_dir=t_dir..'/'.._
- if fs.exists(t_dir) then
- dir=t_dir
- ts[level]={dir,fs.list(dir)}
- index[level]=0
- else
- level=level-1
- end
- else
- dir=t_dir
- ts[level]={dir,fs.list(dir)}
- index[level]=0
- end
- end
- end
- end
- until false
- return path, index
- end
- end
- ---iterator creator, over the valid paths defined by glob @g, e.g */filenumber?to
- -- see the unix part of the table at http://en.wikipedia.org/wiki/Glob_(programming) .
- --@treturn string path to matching of the dir
- --@usage
- --for path in search.iterGlob('*/stuff?/a*') do
- -- print(path)
- --end
- --APIS/stuff1/a.lua
- --var/stuff2/a.var
- function iterGlob(g)
- return iterPatterns(compact(g))
- end
- --iterator over glob @g with ? replaced by @s
- searchGlob = function (g,s)
- g=string.gsub(g,'%?',s)
- local iter=iterGlob(g)
- local path
- return function ()
- repeat
- path=iter()
- if path then
- if not fs.isDir(path) then
- return path
- end
- else
- break
- end
- until false
- end
- end
- function getNameExpansion(s) --returns name and expansion from a filepath @s; special cases: ''->'',nil; '.'-> '','';
- --s string = filename
- --returns: name, expansion
- --Example
- --print(getNameExpansion('filename.lua.kl'))
- --filename
- --lua.kl
- local _,_,name,expa=string.find(s, '([^%./\\]*)%.(.*)$')
- return name or s,expa
- end
- function getDir(s) --returns directory from filepath @s
- return string.match(s,'^(.*)/') or '/'
- end
- --iterator over directory @p searching for @p...@s, where @s is a glob.
- searchTree = function (p,s)
- if not p:match('%?') and fs.isDir(p) then
- if p=='/' then p='' end
- local iter=iterFiles(p)
- local path
- return function ()
- repeat
- path=iter()
- if path then
- if string.match(path,s..'$') and not string.match(path,'[^/]'..s..'$') then
- return path
- end
- else
- break
- end
- until false
- end
- else
- return function() end
- end
- end
- return env]===================],"CoolisOS/ect",true,"CoolisOS/ect/log",true,"CoolisOS/ect/log/loadreq.log",[===================[]===================],"CoolisOS/ect/log/log.log",[===================[42.30 shell-error: CoolisOS/bin/programs/quarry.lua:165: No peripheral attached
- ]===================],"CoolisOS/ect/log/sched.log",[===================[]===================],"CoolisOS/ect/log/shell.log",[===================[42.30 shell-error: CoolisOS/bin/programs/quarry.lua:165: No peripheral attached
- ]===================],"CoolisOS/ect/startups",true,"CoolisOS/ect/startups/quarry.lua",[===================[os.execute('>'..fs.combine(_INSTALL_PATH,'bin/programs/quarry.lua'))
- os.execute('=main()')]===================],"CoolisOS/ect/turtle",true,"CoolisOS/ect/turtle/mode.var",[===================[do local _={attack=true,gps=false,delay=0.5,count=20,dig=true};return _;end]===================],"CoolisOS/ect/turtle/pos-dir.var",[===================[do local _={pos={[1]=4,[2]=4,[3]=-4},dir={[1]=1,[2]=0,[3]=0}};return _;end]===================],
- }
- args={...}
- if args[1]=='help' or args[1]=='-help' or args[1]=='--help' then
- else
- local loc=shell.getRunningProgram()
- local llen=loc:len()
- local nlen=fs.getName(loc):len()
- loc=loc:sub(1,(llen==nlen and 0 or llen-nlen-1))
- print(args[1])
- print(loc)
- local r=args[1] or loc
- for i=1,#t,2 do
- p,v=t[i],t[i+1]
- p=fs.combine(r,p)
- if i~=1 then
- fs.delete(p)
- end
- if type(v)=='boolean' then
- fs.makeDir(p)
- else
- local f=fs.open(p,'w')
- f.write(v)
- f.close()
- end
- end
- local r_s=args[2] and r or ''
- if not fs.exists('startup') then
- fs.delete(fs.combine(r_s,'startup'))
- local f=fs.open(fs.combine(r_s,'startup'),'w')
- f.write(string.format("shell.run('%s')",fs.combine(loc,'CoolisOS/bin/kernel/init.lua')))
- f.close()
- print('Running startup')
- dofile(fs.combine(r_s,'startup'))
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment