CoolisTheName007

Untitled

Apr 22nd, 2013
291
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 347.63 KB | None | 0 0
  1. local t={"CoolisOS",true,"CoolisOS/bin",true,"CoolisOS/bin/kernel",true,"CoolisOS/bin/kernel/compat.lua",[===================[--compat
  2.  
  3. local interface_events={
  4. 'mouse_click',
  5. 'mouse_scroll',
  6. 'mouse_drag',
  7. 'monitor_touch',
  8. 'key',
  9. 'char',
  10. }
  11.  
  12.  
  13. --needs to determine the terminal in some other way.
  14. local check=require'checker'.check
  15.  
  16. local compat={}
  17. function compat.get_pullEventRaw(term_em,plat_em)
  18. return function (filter)
  19. check('?string',filter)
  20. local t=assert(sched.running)
  21. if not t._native then
  22. local pipe=sched.pipe()
  23. t._native=pipe
  24. local h=sched.sighook(function(em,ev,...)
  25. if plat_em and em==plat_em and interface_events[ev] then
  26. return
  27. else
  28. pipe:send{ev,...}
  29. end
  30. end)
  31. if plat_em then h:link(plat_em,'*') end
  32. if term_em then h:link{[term_em]=interface_events} end
  33. end
  34. local pipe=t._native
  35. repeat
  36. local r=pipe:receive()
  37. until (filter==nil or filter==r[1])
  38. return unpack(r)
  39. end
  40. end
  41.  
  42. function compat.adapt_env(env,term_em,plat_em)
  43. env.os.pullEventRaw=compat.get_pullEventRaw(term_em,plat_em)
  44. end
  45.  
  46.  
  47. function compat.adapt_global(_Gnative)
  48. local Box=require'kernel.class.Box'
  49.  
  50. local turtle=_Gnative.turtle
  51. if turtle then
  52. local waitForResponse=function(id)
  53. repeat
  54. local em,ev,rid,p1=sched.wait(sched.platform,'turtle_response')
  55. if em==sched.platform then
  56. if id==rid then
  57. return p1
  58. end
  59. else return false,'timer' end
  60. until false
  61. end
  62. local native=turtle.native or turtle
  63. local turtle={}
  64. rawset(_Gnative,'turtle',turtle)
  65. turtle["getItemCount"] = native.getItemCount
  66. turtle["getItemSpace"] = native.getItemSpace
  67. turtle["getFuelLevel"] = native.getFuelLevel
  68. local function wrap( _sCommand )
  69. return function( ... )
  70. local id = native[_sCommand]( ... )
  71. if id == -1 then
  72. return false
  73. end
  74. return waitForResponse( id )
  75. end
  76. end
  77. for k,v in pairs( native ) do
  78. if type( k ) == "string" and type( v ) == "function" then
  79. if turtle[k] == nil then
  80. turtle[k] = wrap( k )
  81. end
  82. end
  83. end
  84. if peripheral.getType("left") == "workbench" then
  85. turtle["craft"] = function( ... )
  86. local id = peripheral.call( "left", "craft", ... )
  87. return waitForResponse( id )
  88. end
  89. elseif peripheral.getType("right") == "workbench" then
  90. turtle["craft"] = function( ... )
  91. local id = peripheral.call( "right", "craft", ... )
  92. return waitForResponse( id )
  93. end
  94. end
  95.  
  96. end
  97. rednet.receive=function( nTimeout )
  98. local em,e, p1, p2, p3 = sched.wait(sched.platform,'rednet_message',nTimeout)
  99. if em==sched.platform then
  100. return p1, p2, p3
  101. else
  102. return
  103. end
  104. end
  105.  
  106.  
  107. local dyn_k={
  108. 'term',
  109. 'os',
  110. 'shell',
  111. }
  112. local dyn_f={
  113. 'write',
  114. 'printError',
  115. 'read',
  116. }
  117. for _,k in pairs(dyn_f) do
  118. rawset(_Gnative,k,function(...)
  119. local box=assert(Box.get_current(1))
  120. return box.env[k](...)
  121. end)
  122. end
  123. setmetatable(_Gnative,{
  124. __index=function(t,k)
  125. if k=='_BOX' then
  126. return Box.get_current(1)
  127. elseif dyn_k[k] then
  128. local box=assert(Box.get_current(1))
  129. return box.env[k]
  130. else return rawget(t,k) end
  131. end
  132. })
  133.  
  134. end
  135.  
  136. return compat]===================],"CoolisOS/bin/kernel/debug.lua",[===================[--debug
  137. include'kernel.class'
  138. local check=require'kernel.checker'.check
  139. local debug={}
  140.  
  141. local function wrap(text, limit) --from Kingdaroo
  142. local lines = {''}
  143. for word, space in text:gmatch('(%S+)(%s*)') do
  144. local temp = lines[#lines] .. word .. space:gsub('\n','')
  145. if #temp > limit then
  146. table.insert(lines, '')
  147. end
  148. if space:find('\n') then
  149. lines[#lines] = lines[#lines] .. word
  150.  
  151. space = space:gsub('\n', function()
  152. table.insert(lines, '')
  153. return ''
  154. end)
  155. else
  156. lines[#lines] = lines[#lines] .. word .. space
  157. end
  158. end
  159. return lines
  160. end
  161.  
  162. function debug.step_print(s,t)
  163. check('string,Terminal',s,t)
  164. local x,y=t.term:getSize()
  165. y=y-1
  166. local tt=wrap(s,x)
  167. local r=#tt%y
  168. local n=(#tt-r)/y
  169. for i=0,n-1 do
  170. if i~=0 then t:write('\n') end
  171. t:write(table.concat(tt,'\n',i*y+1,(i+1)*y))
  172. t:write('\nPress any key to continue')
  173. repeat until (t.pin:receive())[2]=='char'
  174. if i==n-1 then t:write('\n') end
  175. end
  176. t:write(table.concat(tt,'\n',n*y+1,n*y+r))
  177. t:write('\n')
  178. end
  179.  
  180. function debug.printError(s,t)
  181. check('string,Terminal',s,t)
  182. if t.term:isColour() then
  183. t.term:setTextColour( colours.red )
  184. end
  185. debug.step_print(s,t)
  186. t.term:setTextColour( colours.white )
  187. end
  188.  
  189. --debug.traceback
  190. do
  191. --[[ Notes:
  192. getfenv(1)=getfenv()~=getfenv(0)~~_G
  193. error('',1)~=error('',0)
  194. ]]
  195. rawset(_G,'loadfile',function( _sFile )
  196. local file = fs.open( _sFile, "r" )
  197. if file then
  198. local func, err = loadstring( file.readAll(),_sFile)
  199. file.close()
  200. return func, err
  201. end
  202. return nil, "File not found"
  203. end)
  204. local traceback=function(level,terr,maxlevel)
  205. local terr=terr or {}
  206. local passed={}
  207. local err,ok=terr
  208. local j=2+(level or 0)
  209. repeat
  210. j=j+1
  211. passed[err]=true
  212. ok,err=pcall(error,'',j)
  213. --if err:match('^[^:]+')=='bios' then break end
  214. table.insert(terr,err)
  215. until (passed[err] or (maxlevel and (j+1-2>maxlevel) ) )
  216.  
  217. return terr
  218. end
  219.  
  220. local function classify(t,f)
  221. local bins={}
  222. local c
  223. for i,v in ipairs(t) do
  224. local fi,r=f(v)
  225. if fi~=nil then
  226. if c==nil or c.n~=fi then
  227. table.insert(bins,c)
  228. c={n=fi}
  229. end
  230. table.insert(c,r)
  231. end
  232. end
  233. table.insert(bins,c)
  234. return bins
  235. end
  236. local function get_chunks(terr)
  237. return classify(terr,function(v) return v:match('^([^:]+):(.*)') end)
  238. end
  239.  
  240. local function get_lines(terr)
  241. return classify(terr,function(v) return v:match('^(%d+):(.*)') end)
  242. end
  243.  
  244. local function put_lines(path,diameter,ts,l)
  245. local ok,f=pcall(fs.open,path,'r')
  246. if not ok or not f then table.insert(ts,' (could not open file)')
  247. else
  248. for i=1,l-diameter-1 do
  249. f.readLine()
  250. end
  251. for i=1,diameter do
  252. table.insert(ts,' '..f.readLine())
  253. end
  254. table.insert(ts,'->'..f.readLine())
  255. for i=1,diameter do
  256. local l=f.readLine()
  257. if l then table.insert(ts,' '..l)
  258. else break end
  259. end
  260. end
  261. end
  262.  
  263. local function format_traceback(terr)
  264. local ts={}
  265. for i,v in ipairs(get_chunks(terr)) do
  266. local name=v.n or '(no name)'
  267. local path
  268. if fs.exists(name) then
  269. name,dir,path=fs.getName(name),name:match('^(.*)/') or '/',name
  270. name=name..' ('..(dir or 'unknown')..')'
  271. elseif false then
  272. --shell has input string; modify to behave as file?
  273. end
  274. table.insert(ts,name)
  275. for j,line in ipairs(get_lines(v)) do
  276. table.insert(ts,' line '..(line.n or '(no line)')..', *'..(#line))
  277. local n=line.n and tonumber(line.n) or 1
  278. if path and n then
  279. put_lines(path,1,ts,n)
  280. end
  281. end
  282. end
  283. return ts
  284. end
  285.  
  286. function debug.traceback(msg,level,maxlevel)
  287. local level=level or 0
  288. local ts=format_traceback(traceback(level+1,{},maxlevel))
  289. msg=(msg and '\n' or '')..'['..(msg or 'debug.traceback')
  290. table.insert(ts,1,msg)
  291. return table.concat(ts,'\n')..'\n]'
  292. end
  293.  
  294. function debug.printTraceback(msg,level)
  295. debug.printError(debug.traceback(msg,(level or 0)+1))
  296. end
  297.  
  298. function debug.tracebackError(msg,level)
  299. error(debug.traceback(msg,(level or 0)+1),2)
  300. end
  301. end
  302. function debug.raw(t)
  303. if type(t)=='table' then
  304. local mt=getmetatable(t)
  305. if mt then
  306. local f=rawget(mt,'__tostring')
  307. rawset(mt,'__tostring',nil)
  308. local s=tostring(t)
  309. rawset(mt,'__tostring',f)
  310. return s
  311. else
  312. return tostring(t)
  313. end
  314. else
  315. return tostring(t)
  316. end
  317. end
  318. function debug.log(...)
  319. return log('debug','DEBUG','(%s):\nMsg:\n\t%s\nFrom:%s',sched.running or 'nil',debug.args_tostring(...),debug.traceback('',1,2))
  320. end
  321.  
  322.  
  323. function debug.objinfo(s)
  324. local name=debug.named[s]
  325. return (name and name..'|' or '')
  326. ..(typeof(s)=='object' and (classname(s) or debug.raw(s))..' instance|' or '')
  327. ..debug.raw(s) or ''
  328. end
  329.  
  330.  
  331. function debug.getinfo(v)
  332. if type(v)=='table' then
  333. return debug.objinfo(v)
  334. else
  335. return type(v)..':'..tostring(v)
  336. end
  337. end
  338. function debug.args_tostring(...)
  339. local args = {}
  340. local t = {...}
  341. for k = 1, table.maxn(t) do table.insert(args, (debug.getinfo)(t[k])) end
  342. return table.concat(args,',')
  343. end
  344. function debug.wrap(f,silent)
  345. return function(...)
  346. --wrapped level
  347. local r={xpcall(f,debug.traceback)}
  348. local ok=table.remove(r,1)
  349. if not ok then
  350. local er=r[1]
  351. if silent then return nil,er
  352. else error(er,2) end
  353. end
  354. return unpack(r)
  355. end
  356. end
  357.  
  358. debug.named=setmetatable({},{__mode='kv'})
  359. function debug.name(obj,name)
  360. debug.named[obj]=name
  361. return obj
  362. end
  363.  
  364. function debug.namefield(self,k,v)
  365. v=v==nil and self[k] or v
  366. check('table,!nil,!nil',self,k,v)
  367. self[k]=v
  368. debug.name(v,('(%s).(%s)'):format(debug.getinfo(self),debug.getinfo(k)))
  369. return v
  370. end
  371.  
  372. debug.say=function(s) return log('init','INFO','task:(%s) says:%s',tostring(sched.running),s) end
  373.  
  374. return debug]===================],"CoolisOS/bin/kernel/driver.lua",[===================[local driver={}
  375.  
  376. local peripheral=peripheral
  377. function driver.update()
  378. for _, side in ipairs(rs.getSides()) do
  379. if peripheral.isPresent(side) then
  380. driver[side]=peripheral.wrap(side)
  381. -- sched.signal(peripheral,peripheral[side].getType(),side)
  382. else
  383. driver[side]=nil
  384. end
  385. end
  386. end
  387.  
  388. return driver]===================],"CoolisOS/bin/kernel/init.lua",[===================[local path=shell.getRunningProgram()
  389. local function override(f)
  390. local t={}
  391. local fo=printError
  392. local key='printError'
  393. local co=coroutine.status
  394. local wrapped=function(s)
  395. coroutine.status=co
  396. for i,v in pairs(t) do
  397. rawset(v,key,fo)
  398. end
  399. local ok,err=pcall(f)
  400. if not ok then
  401. fo(err)
  402. end
  403. coroutine.yield()
  404. coroutine.yield()
  405. end
  406. for i=1,20 do
  407. local ok,err=pcall(getfenv,i)
  408. if ok then
  409. if rawget(err,key)~=nil and not t[err] then
  410. t[i]=err
  411. rawset(err,key,wrapped)
  412. end
  413. end
  414. end
  415. coroutine.status=nil
  416. repeat
  417. os.pullEvent()
  418. until false
  419. end
  420.  
  421. local function fixes()
  422. local string=string
  423. local rawset, rawget,type=rawset, rawget,type
  424. local nativegetmetatable=getmetatable
  425. local s_meta=getfenv(('').sub)
  426. rawset(_G,'getmetatable',nil)
  427. local function getmetatable(t)
  428. if type(t)=='string' then
  429. return s_meta
  430. else
  431. return nativegetmetatable(t)
  432. end
  433. end
  434. rawset(_G,'getmetatable',getmetatable)
  435.  
  436. local nativesetmetatable = setmetatable
  437. rawset(_G,'setmetatable',nil)
  438. local function setmetatable( _o, _t )
  439. if _t and type(_t) == "table" then
  440. local idx = rawget( _t, "__index" )
  441. local newidx=rawget( _t, "__newindex" )
  442. rawset(_t,"__index",nil)
  443. rawset(_t,"__newindex",nil)
  444. local r= nativesetmetatable( _o, _t )
  445. rawset(_t,"__index",idx)
  446. rawset(_t,"__newindex",newidx)
  447. local meta=getmetatable(_o)
  448. rawset(meta,"__index",idx)
  449. rawset(meta,"__newindex",newidx)
  450. return r
  451. else
  452. return nativesetmetatable( _o, _t )
  453. end
  454. end
  455. rawset(_G,'setmetatable',setmetatable)
  456.  
  457. rawset(_G,'loadfile',function( _sFile )
  458. local file = fs.open( _sFile, "r" )
  459. if file then
  460. local func, err = loadstring( file.readAll(),_sFile)
  461. file.close()
  462. return func, err
  463. end
  464. return nil, "File not found"
  465. end)
  466.  
  467. end
  468.  
  469. local function start()
  470. debug.name(sched.running,'shell')
  471. --log.setLevel('sched','DEBUG')
  472. log.setLevel('DEBUG','debug')
  473. --log.setLevel('DEBUG','pipe')
  474. --log.setLevel('ALL')
  475. --log.store.instant=true
  476. require'kernel.compat'.adapt_global(_G)
  477. local gui=require'kernel.gui'
  478.  
  479. local terminal=gui.Terminal.from_native(_G.term,sched.platform)
  480. local shell=require'kernel.gui.shell'
  481.  
  482. local Box=require'kernel.class.Box'
  483. Box{pin=terminal.pin,pout=terminal.pout,terminal=terminal}:run(
  484. function()
  485. local s=shell.new()
  486. s.run_startups()
  487. end)
  488. sched.wait('end')
  489. end
  490.  
  491. local main=function()
  492. --save copy of old _G
  493. rawset(_G,'_Gstore',{
  494. old=(function()
  495. local c={}
  496. for i,v in pairs(_G) do
  497. c[i]=v
  498. end
  499. return c
  500. end)(),
  501. base=_G,
  502. base_env=getfenv(),
  503. })
  504. --overrides
  505. fixes()
  506.  
  507. ---set install variables and module loading utilities
  508. do
  509. _FILE_PATH=path
  510. local loc=(_FILE_PATH:match'(.*)bin/kernel/init%.lua'):sub(1,-2)
  511. rawset(_G,'_INSTALL_PATH',loc)
  512. rawset(_G,'loadreq',dofile(fs.combine(_INSTALL_PATH,'bin/kernel/loadreq/init.lua')))
  513. rawset(_G,'require',loadreq.require)
  514. rawset(_G,'include',loadreq.include)
  515. loadreq.vars.paths=loadreq.vars.paths:gsub('%?',fs.combine(_INSTALL_PATH,'bin')..'/%?')..';'..loadreq.vars.paths..';'..loadreq.vars.paths:gsub('%?',_INSTALL_PATH..'/%?')
  516. end
  517.  
  518. ---load modules
  519. do
  520. local function G(k,v)
  521. rawset(_G,k,v)
  522. end
  523. G('util',require'kernel.util')
  524. G('log',require'kernel.log')
  525. --log.store=require'kernel.log.store'
  526. --log.store.reset()
  527. do
  528. local serpent=require'utils.serpent'
  529. G('pstring',function(this,max_level) return serpent.serialize(this,{debug=true,indent = ' ', sortkeys = true, comment = true,maxlevel=max_level}) end)
  530. G('pprint',function(this,max_level)
  531. debug.step_print(pstring(this),getfenv(2).term)
  532. end)
  533. G('debug',require'kernel.debug')
  534. G('log',require'kernel.log')
  535. local globals={
  536. -- read=require'utils.tab_read',
  537. net=require'kernel.net',
  538. sched=require'kernel.sched',
  539. }
  540. for i,v in pairs(globals) do G(i,v) end
  541. for i,v in pairs(require'kernel.class') do
  542. G(i,v)
  543. end
  544. end
  545.  
  546.  
  547. --overrided to os
  548. do
  549. os.sleep(0)
  550. local o_os=os
  551. local check=require'kernel.checker'.check
  552. local os=util.shcopy(o_os)
  553. local function wrap(f,i,e)
  554. return function(...)
  555. local ir,fr=nil,nil
  556. if i then ir={i(...)} end
  557. fr={f(...)}
  558. if e then e(fr,ir) end
  559. return unpack(fr)
  560. end
  561. end
  562. os.version=function()
  563. return 'CoolisOS.1'
  564. end
  565. os.sleep=function(n)
  566. if sched.running then
  567. check('number',n) sched.wait(n)
  568. else return o_os.sleep(n) end
  569. end
  570. rawset(_G,'sleep',os.sleep)
  571.  
  572. rawset(_G,'os',os)
  573. end
  574. end
  575.  
  576. sched.task(function()
  577. debug.wrap(start)()
  578. end):run()
  579.  
  580. sched.loop()
  581. log.store.flush()
  582. print'Press any key to shutdown'
  583. sleep(2)
  584. os.pullEvent'key'
  585. os.shutdown()
  586. end
  587.  
  588. override(function()
  589. local ok,er=xpcall(main,function(msg,level)
  590. if _G.debug then
  591. return _G.debug.traceback(msg)
  592. else
  593. return msg
  594. end
  595. end)
  596. if not ok then
  597. if log then
  598. log('init','ERROR','%s',er)
  599. error(er)
  600. end
  601. end
  602. end)]===================],"CoolisOS/bin/kernel/net.lua",[===================[local string_mt=getfenv(('').len)
  603. local net=string_mt.__net or {}
  604. string_mt.__net=net
  605. net.ids=net.ids or setmetatable({},{__mode='v'})
  606. local ids=net.ids
  607. local id=os.getComputerID()
  608. ids[id]=_G
  609.  
  610. net.queueEvent=function(id,...)
  611. if net.ids[id] then
  612. ok,err=pcall(net.ids[id].os.queueEvent,...)
  613. if not ok then
  614. net.ids[id]=nil
  615. return
  616. end
  617. return true
  618. end
  619. end
  620. net.queueAll=function(...)
  621. local t={}
  622. for id in pairs(net.ids) do
  623. t[id]=net.queueEvent(id,...)
  624. end
  625. return t
  626. end
  627. net.rednet={
  628. send=function(id,msg)
  629. return net.queueEvent(id,'rednet_message',os.getComputerID(),msg,nil)
  630. end,
  631. broadcast=function(id,msg)
  632. return net.queueAll('rednet_message',os.getComputerID(),msg,nil)
  633. end,
  634. }
  635.  
  636. return net]===================],"CoolisOS/bin/kernel/util.lua",[===================[--- ## Moses: <em>A utility-belt library for functional programming in Lua.</em><br/>
  637. -- @author Roland_Yonaba
  638. -- @copyright 2012
  639. -- @license [MIT](http://www.opensource.org/licenses/mit-license.php)
  640. -- @script moses
  641. -- Internalisation
  642. local error,pcall=error,pcall
  643. local next, type, unpack, select = next, type, unpack, select
  644. local setmetatable, getmetatable = setmetatable, getmetatable
  645. local t_insert, t_sort = table.insert, table.sort
  646. local t_remove,t_concat = table.remove, table.concat
  647. local randomseed, random, huge = math.randomseed, math.random, math.huge
  648. local floor, max, min = math.floor, math.max, math.min
  649. local getfenv = getfenv
  650. local unpack = unpack
  651. local pairs,ipairs = pairs,ipairs
  652. local _ = {}
  653.  
  654.  
  655. -- ======== Private helpers
  656.  
  657. local function f_max(a,b) return a>b end
  658. local function f_min(a,b) return a<b end
  659. local function clamp(var,a,b) return (var<a) and a or (var>b and b or var) end
  660. local function isTrue(_,value) return value and true end
  661. local function iNot(value) return not value end
  662. local function count(t)
  663. local i
  664. for k,v in pairs(t) do i = (i or 0) + 1 end
  665. return i
  666. end
  667. local function extract(list,comp,transform,...)
  668. local _ans
  669. local transform = transform or _.identity
  670. for index,value in pairs(list) do
  671. if not _ans then _ans = transform(value,...)
  672. else
  673. local value = transform(value,...)
  674. _ans = comp(_ans,value) and _ans or value
  675. end
  676. end
  677. return _ans
  678. end
  679.  
  680. -- Internal counter for unique ids generation
  681. local unique_id_counter = -1
  682. -- ========= Math utilities
  683. 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
  684.  
  685.  
  686. -- ========= Filesystem utilities
  687. function _.with(handle,f,...)
  688. local ok, err=pcall(f,handle,...)
  689. if handle and handle.close then handle:close() end
  690. if not ok then error('util.with:'..err,2) end
  691. return ok,err
  692. end
  693. -- ========== Table utilities
  694. function _.set(t,...)
  695. local arg={...}
  696. local t,n=t,#arg
  697. for i=1,(n-2) do
  698. t[arg[i]]=t[arg[i]] or {}
  699. t=t[arg[i]]
  700. end
  701. t[arg[n-1]]=arg[n]
  702. end
  703.  
  704. function _.get(t,...)
  705. local arg={...}
  706. for i=1,#arg do
  707. t=t[arg[i]]
  708. if t==nil then return nil end
  709. end
  710. end
  711.  
  712. function _.delete(t,...)
  713. local arg={...}
  714. local n=#arg
  715. for i=1,n-1 do
  716. t=t[arg[i]]
  717. if t==nil then return nil end
  718. end
  719. t[arg[n]]=nil
  720. end
  721.  
  722. function _.map_args(t,f,...)
  723. local args = {}
  724. for k = 1, table.maxn(t) do table.insert(args,f(t[k],...)) end
  725. return args
  726. end
  727.  
  728.  
  729. -- ========= Scheduler utilities
  730. function _.wait(f,match,...)
  731. local r,b
  732. repeat
  733. r={f(...)}
  734. b=true
  735. for i,v in pairs(match) do
  736. if r[i]~=nil and r[i]~=v then
  737. b=false
  738. break
  739. end
  740. end
  741. until b
  742. return r
  743. end
  744.  
  745.  
  746. -- ========= Table functions
  747. --------------------------------------------------------------------------------
  748. ---nils a table of arbitrary keys/values
  749. -- @function [parent=#utils.table] tnil
  750. -- @param table to nil
  751. function _.tnil(t)
  752. local del=next(t)
  753. if del then
  754. for i,v in next,t,del do
  755. t[del]=nil
  756. del=i
  757. end
  758. t[del]=nil
  759. end
  760. end
  761. function _.shcopy(src, dst)
  762. dst = dst or {}
  763. for k, v in pairs(src) do
  764. dst[k]=v
  765. end
  766. return dst
  767. end
  768. --------------------------------------------------------------------------------
  769. -- Copies a table from the source to destination table.
  770. --
  771. -- The copy is recursive.
  772. --
  773. -- @function [parent=#utils.table] copy
  774. -- @param src a table to be used as the source of the copy.
  775. -- @param dst a table to be used as the destination of the copy.
  776. -- @param overwrite boolean value, to enable overwrite of existing field in dst table.
  777. -- @return the resulting dst table.
  778. --
  779. local _copy
  780. _copy=function(passed,src, dst, overwrite)
  781. dst = dst or {}
  782. passed[src]=dst
  783. for k, v in pairs(src) do
  784. if type(v) == 'table' then -- recursively go into tables
  785. if passed[v] then
  786. dst[k]=passed[v]
  787. else
  788. if not (type(dst[k]) == 'table') and (overwrite or dst[k]==nil) then
  789. dst[k] = {}
  790. end
  791. _copy(passed,v, dst[k], overwrite)
  792. end
  793. elseif overwrite or dst[k]==nil then dst[k] = v end
  794. end
  795.  
  796. return dst
  797. end
  798. _.copy=function(...) return _copy({},...) end
  799. --------------------------------------------------------------------------------
  800. -- Produces a diff of two tables.
  801. --
  802. -- Recursive diff by default.
  803. --
  804. -- @function [parent=#utils.table] diff
  805. -- @param t1 first table to compare.
  806. -- @param t2 second table to compare.
  807. -- @param norecurse boolean value to disable recursive diff.
  808. -- @return the diff result as a table.
  809. -- @usage
  810. --> t1 = { a = 1, b=3, c = "foo"}
  811. --> t2 = { a = 1, b=3, c = "foo"}
  812. --> :diff(t1, t2)
  813. --= { }
  814. --> t1 = { a = 1, b=3, c = "foo"}
  815. --> t2 = { a = 1, b=4, c = "foo"}
  816. --> :diff(t1, t2)
  817. --= { "b" }
  818. --> t1 = { a = 1, b=3, c = "foo"}
  819. --> t2 = { a = 1, b=3}
  820. --> :diff(t1, t2)
  821. --= { "c" }
  822. --> t1 = { b=3, c = "foo", d="bar"}
  823. --> t2 = { a = 1, b=3, c = "foo"}
  824. --> :diff(t1, t2)
  825. --= {
  826. -- "a",
  827. -- "d" }
  828. --
  829.  
  830. function _.diff(t1, t2, norecurse)
  831. local d = {}
  832. local t3 = {}
  833.  
  834. local rpairs = norecurse and pairs or _.recursivepairs
  835. for k, v in rpairs(t1) do t3[k] = v end
  836. for k, v in rpairs(t2) do
  837. if v ~= t3[k] then table.insert(d, k) end
  838. t3[k] = nil
  839. end
  840.  
  841. for k, v in pairs(t3) do table.insert(d, k) end
  842.  
  843. return d
  844. end
  845.  
  846. --- Iterators
  847. -- @section It
  848.  
  849. --------------------------------------------------------------------------------
  850. -- Iterator that iterates on the table in key ascending order.
  851. --
  852. -- @function [parent=#utils.table] sortedPairs
  853. -- @param t table to iterate.
  854. -- @return iterator function.
  855. --
  856.  
  857. function _.sortedpairs(t)
  858. local a = {}
  859. local insert = table.insert
  860. for n in pairs(t) do insert(a, n) end
  861. table.sort(a)
  862. local i = 0
  863. return function()
  864. i = i + 1
  865. return a[i], t[a[i]]
  866. end
  867. end
  868.  
  869. --------------------------------------------------------------------------------
  870. -- Iterator that returns key/value pairs except walking through sub tables and concatenating the path in the key.
  871. --
  872. -- This iterator break cycles: it does not recur. If it detects a cycle, the entry causing a cycle is ignored.
  873. -- However, if a table is repeated, its contents will be repeated by this function.
  874. --
  875. -- @function [parent=#utils.table] recursivePairs
  876. -- @param t table to iterate.
  877. -- @param prefix path prefix to prepend to the key path returned.
  878. -- @return iterator function.
  879. -- @usage {toto={titi=1, tutu=2}, tata = 3, tonton={4, 5}} will iterate through
  880. -- ("toto.titi",1), ("toto.tutu",2), ("tata",3) ("tonton.1", 4), ("tonton.2"=5)
  881. --
  882.  
  883. function _.recursivepairs(t, prefix)
  884. check('table,?|string',t, prefix)
  885. local function it(t, prefix, cp)
  886. cp[t] = true
  887. local pp = prefix == "" and prefix or "."
  888. for k, v in pairs(t) do
  889. k = pp..tostring(k)
  890. if type(v) == 'table' then
  891. if not cp[v] then it(v, prefix..k, cp) end
  892. else
  893. coroutine.yield(prefix..k, v)
  894. end
  895. end
  896. cp[t] = nil
  897. end
  898.  
  899. prefix = prefix or ""
  900. return coroutine.wrap(function() it(t, cleanpath(prefix), {}) end)
  901. end
  902.  
  903. --------------------------------------------------------------------------------
  904. -- Iterator that returns key/value on multiple tables.
  905. --
  906. -- @function [parent=#utils.map] multiPairs
  907. -- Tables are traversed in the same order as they are passed as parameters.
  908. -- @param t1 first table to traverse.
  909. -- @param ... additional tables to traverse.
  910. -- @return iterator function.
  911. --
  912.  
  913. function _.multipairs(t1, ...)
  914. local state = { tables = {t1, ...}, i=1, k=next(t1) }
  915. local function f()
  916. if not state.i then return end
  917. local current_table = state.tables[state.i]
  918. local k, v = state.k, current_table[state.k]
  919. local next_k = next(current_table, k)
  920. while next_k==nil do
  921. state.i = state.i+1
  922. local next_table = state.tables [state.i]
  923. if not next_table then state.i=false; break
  924. else next_k = next (next_table) end
  925. end
  926. state.k = next_k
  927. return k, current_table[k]
  928. end
  929. return f, nil, nil
  930. end
  931.  
  932. -- ========= Collection functions
  933.  
  934. --- Calls function `f` on each key-value of a given collection. <br/><em>Aliased as @{forEach}</em>.
  935. -- @name each
  936. -- @tparam table list a collection
  937. -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
  938. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  939. -- @see forEach
  940. function _.each(list, f, ...)
  941. if not _.isObject(list) then return end
  942. for index,value in pairs(list) do
  943. f(index,value,...)
  944. end
  945. return list
  946. end
  947.  
  948. --- Alias for @{each}.
  949. -- @function forEach
  950. -- @param list
  951. -- @param f
  952. -- @param[opt] ...
  953. _.forEach = _.each
  954.  
  955. --- Maps function `f` on each key-value of a given collection. Collects
  956. -- and returns the outputs.<br/><em>Aliased as @{collect}</em>.
  957. -- @name map
  958. -- @tparam table list a collection
  959. -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
  960. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  961. -- @treturn table a table of results
  962. -- @see collect
  963. function _.map(list, f, ...)
  964. local _list = {}
  965. for index,value in pairs(list) do
  966. _list[index] = f(index,value,...)
  967. end
  968. return _list
  969. end
  970.  
  971. --- Alias for @{map}.
  972. -- @function collect
  973. -- @param list
  974. -- @param f
  975. -- @param[opt] ...
  976. _.collect = _.map
  977.  
  978. --- Reduces an entire collection. Folds from left to right to a single value,
  979. -- with respect to a given iterator and an initial state.
  980. -- The given function takes a state and a value, and returns a new state.
  981. -- <br/><em>Aliased as @{inject}, @{foldl}</em>.
  982. -- @name reduce
  983. -- @tparam table list a collection
  984. -- @tparam function f an iterator function, prototyped as `f(state,value)`
  985. -- @tparam[opt] state state an initial state of reduction. Defaults to the first value in the list.
  986. -- @treturn state state a final state of reduction
  987. -- @see inject
  988. -- @see foldl
  989. function _.reduce(list,f,state)
  990. for _,value in pairs(list) do
  991. state = not state and value or f(state,value)
  992. end
  993. return state
  994. end
  995.  
  996. --- Alias for @{reduce}.
  997. -- @function inject
  998. -- @param list
  999. -- @param f
  1000. -- @param[opt] state
  1001. _.inject = _.reduce
  1002.  
  1003. --- Alias for @{reduce}.
  1004. -- @function foldl
  1005. -- @param list
  1006. -- @param f
  1007. -- @param[opt] state
  1008. _.foldl = _.reduce
  1009.  
  1010. --- Reduces an entire collection. Folds from right to left to a single value,
  1011. -- with respect to a given iterator and an initial state.
  1012. -- The given function takes a state and a value, and returns a new state.
  1013. -- <br/><em>Aliased as @{injectr}, @{foldr}</em>.
  1014. -- @name reduceRight
  1015. -- @tparam table list a collection
  1016. -- @tparam function f an iterator function, prototyped as `f(state,value)`
  1017. -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the list.
  1018. -- @treturn state state a final state of reduction
  1019. -- @see injectr
  1020. -- @see foldr
  1021. function _.reduceRight(list,f,state)
  1022. return _.reduce(_.reverse(list),f,state)
  1023. end
  1024.  
  1025. --- Alias for @{reduceRight}.
  1026. -- @function injectr
  1027. -- @param list
  1028. -- @param f
  1029. -- @param[opt] state
  1030. _.injectr = _.reduceRight
  1031.  
  1032. --- Alias for @{reduceRight}.
  1033. -- @function foldr
  1034. -- @param list
  1035. -- @param f
  1036. -- @param[opt] state
  1037. _.foldr = _.reduceRight
  1038.  
  1039. --- Reduces a collection while saving intermediate states.
  1040. -- Folds the collection from left to right to a single value,
  1041. -- with respect to a given iterator and an initial state.
  1042. -- The given function takes a state and a value, and returns a new state.
  1043. -- The function returns an array of intermediate states.
  1044. -- <br/><em>Aliased as @{mapr}</em>
  1045. -- @name mapReduce
  1046. -- @tparam table list a collection
  1047. -- @tparam function f an iterator function, prototyped as `f(state,value)`
  1048. -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the list.
  1049. -- @treturn table an array of states
  1050. -- @see mapr
  1051. function _.mapReduce(list,f,state)
  1052. local t = {}
  1053. for i,value in pairs(list) do
  1054. t[i] = not state and value or f(state,value)
  1055. state = t[i]
  1056. end
  1057. return t
  1058. end
  1059.  
  1060. --- Alias for @{mapReduce}.
  1061. -- @function mapr
  1062. -- @param list
  1063. -- @param f
  1064. -- @param[opt] state
  1065. _.mapr = _.mapReduce
  1066.  
  1067. --- Reduces a collection while saving intermediate states. It proceeds in reverse order,
  1068. -- folding the collection from right to left to a single value,
  1069. -- with respect to a given iterator and an initial state.
  1070. -- The given function takes a state and a value, and returns a new state.
  1071. -- The function returns an array of intermediate states.
  1072. -- <br/><em>Aliased as @{maprr}</em>
  1073. -- @name mapReduceRight
  1074. -- @tparam table list a collection
  1075. -- @tparam function f an iterator function, prototyped as `f(state,value)`
  1076. -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the list.
  1077. -- @treturn table an array of states
  1078. -- @see maprr
  1079. function _.mapReduceRight(list,f,state)
  1080. return _.mapReduce(_.reverse(list),f,state)
  1081. end
  1082.  
  1083. --- Alias for @{mapReduceRight}.
  1084. -- @function maprr
  1085. -- @param list
  1086. -- @param f
  1087. -- @param[opt] state
  1088. _.maprr = _.mapReduceRight
  1089.  
  1090. --- Looks for an object in a collection. <br/><em>Aliased as @{any}, @{some}</em>
  1091. -- @name include
  1092. -- @tparam table list a collection
  1093. -- @tparam object item a value to be searched
  1094. -- @treturn boolean a boolean : __true__ when found, __false__ otherwise
  1095. -- @see any
  1096. -- @see some
  1097. function _.include(list,item)
  1098. local _iter = _.isFunction(item) and item or _.isEqual
  1099. for _,value in pairs(list) do
  1100. if _iter(value,item) then return true end
  1101. end
  1102. return false
  1103. end
  1104.  
  1105. --- Alias for @{include}.
  1106. -- @function any
  1107. -- @param list
  1108. -- @param item
  1109. _.any = _.include
  1110.  
  1111. --- Alias for @{include}.
  1112. -- @function some
  1113. -- @param list
  1114. -- @param item
  1115. _.some = _.include
  1116.  
  1117. --- Looks for an object index in a collection. <br/><em>Aliased as @{where}, @{find}</em>
  1118. -- @name detect
  1119. -- @tparam table list a collection
  1120. -- @tparam object item a value to be searched
  1121. -- @treturn key the object key or __nil__
  1122. -- @see find
  1123. -- @see where
  1124. function _.detect(list,item)
  1125. local _iter = _.isFunction(item) and item or _.isEqual
  1126. for key,arg in pairs(list) do
  1127. if _iter(arg,item) then return key end
  1128. end
  1129. end
  1130.  
  1131. --- Alias for @{detect}.
  1132. -- @function find
  1133. -- @param list
  1134. -- @param item
  1135. _.find = _.detect
  1136.  
  1137. --- Alias for @{detect}.
  1138. -- @function where
  1139. -- @param list
  1140. -- @param item
  1141. _.where = _.detect
  1142.  
  1143. --- Selects and extracts objects passing an iterator test.
  1144. -- <br/><em>Aliased as @{filter}</em>
  1145. -- @name select
  1146. -- @tparam table list a collection
  1147. -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
  1148. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  1149. -- @treturn table the selected objects
  1150. -- @see filter
  1151. function _.select(list,f,...)
  1152. local _mapped = _.map(list,f,...)
  1153. local _list = {}
  1154. for index,value in pairs(_mapped) do
  1155. if value then _list[#_list+1] = list[index] end
  1156. end
  1157. return _list
  1158. end
  1159.  
  1160. --- Alias for @{select}.
  1161. -- @function filter
  1162. -- @param list
  1163. -- @param f
  1164. -- @param[opt] ...
  1165. _.filter = _.select
  1166.  
  1167. --- Clones a collection, rejecting objects passing an iterator test.
  1168. -- <br/><em>Aliased as @{discard}</em>
  1169. -- @name reject
  1170. -- @tparam table list a collection
  1171. -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
  1172. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  1173. -- @treturn table the remaining objects
  1174. -- @see discard
  1175. function _.reject(list,f,...)
  1176. local _mapped = _.map(list,f,...)
  1177. local _list = {}
  1178. for index,value in pairs (_mapped) do
  1179. if not value then _list[#_list+1] = list[index] end
  1180. end
  1181. return _list
  1182. end
  1183.  
  1184. --- Alias for @{reject}.
  1185. -- @function discard
  1186. -- @param list
  1187. -- @param f
  1188. -- @param[opt] ...
  1189. _.discard = _.reject
  1190.  
  1191. --- Tests if all objects in a collection passes an iterator test. <br/><em>Aliased as @{every}</em>
  1192. -- @name all
  1193. -- @tparam table list a collection
  1194. -- @tparam function f an iterator function, prototyped as `f(key,value,...)`
  1195. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  1196. -- @treturn boolean __true__ or __false__
  1197. -- @see every
  1198. function _.all(list,f,...)
  1199. return ((#_.select(_.map(list,f,...), isTrue)) == (#list))
  1200. end
  1201.  
  1202. --- Alias for @{all}.
  1203. -- @function every
  1204. -- @param list
  1205. -- @param f
  1206. -- @param[opt] ...
  1207. _.every = _.all
  1208.  
  1209. --- Invokes a method on each object in a collection.
  1210. -- @name invoke
  1211. -- @tparam table list a collection
  1212. -- @tparam function method a function, prototyped as `f(object,...)`
  1213. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `method`
  1214. -- @treturn table an array of results
  1215. function _.invoke(list,method,...)
  1216. local args = {...}
  1217. return _.map(list, function(__,v)
  1218. if _.isObject(v) then
  1219. if _.has(v,method) then
  1220. if _.isCallable(v[method]) then
  1221. return v[method](v,unpack(args))
  1222. else return v[method]
  1223. end
  1224. else
  1225. if _.isCallable(method) then
  1226. return method(v,unpack(args))
  1227. end
  1228. end
  1229. elseif _.isCallable(method) then
  1230. return method(v,unpack(args))
  1231. end
  1232. end)
  1233. end
  1234.  
  1235. --- Extracts property-values from a collection of objects.
  1236. -- @name pluck
  1237. -- @tparam table list a collection
  1238. -- @tparam string a property, to index in each object: `object[property]`
  1239. -- @treturn table an array of values for the specified property
  1240. function _.pluck(list,property)
  1241. return _.reject(_.map(list,function(__,value)
  1242. return value[property]
  1243. end),
  1244. iNot)
  1245. end
  1246.  
  1247. --- Returns the maximum value in a collection. If an iterator is passed, it will
  1248. -- be used to extract the property value by which all objects will be ranked.
  1249. -- @name max
  1250. -- @tparam table list a collection
  1251. -- @tparam[opt] function iter an iterator function, prototyped as `iter(value,...)`, defaults to @{identity}
  1252. -- @tparam[optchain] var_arg ... Optional extra-args to be passed to function `iter`
  1253. -- @treturn value the maximum property value found
  1254. function _.max(list,iter,...)
  1255. return extract(list,f_max,iter,...)
  1256. end
  1257.  
  1258. --- Returns the minimum value in a collection. If an iterator is passed, it will
  1259. -- be used to extract the property value by which all objects will be ranked.
  1260. -- @name min
  1261. -- @tparam table list a collection
  1262. -- @tparam[opt] function iter an iterator function, prototyped as `iter(value,...)`, defaults to @{identity}
  1263. -- @tparam[optchain] var_arg ... Optional extra-args to be passed to function `iter`
  1264. -- @treturn value the minimum property value found
  1265. function _.min(list, iter,...)
  1266. return extract(list,f_min, iter,...)
  1267. end
  1268.  
  1269. --- Returns a shuffled copy of a given collection. If a seed is provided, it will
  1270. -- be used to init the random number generator.
  1271. -- @name shuffle
  1272. -- @tparam table list a collection
  1273. -- @tparam[opt] number seed a seed
  1274. -- @treturn table a shuffled copy of the given collection
  1275. function _.shuffle(list,seed)
  1276. if seed then randomseed(seed) end
  1277. local _shuffled = {}
  1278. _.each(list,function(index,value)
  1279. local randPos = floor(random()*index)+1
  1280. _shuffled[index] = _shuffled[randPos]
  1281. _shuffled[randPos] = value
  1282. end)
  1283. return _shuffled
  1284. end
  1285.  
  1286. --- Tests if two collections are the same. I.e, they feature the same objects, but not
  1287. -- necessarily at the same keys
  1288. -- @name same
  1289. -- @tparam table a a collection
  1290. -- @tparam table b a collection
  1291. -- @treturn boolean __true__ or __false__
  1292. function _.same(a,b)
  1293. return _.all(a, function (i,v)
  1294. return _.include(b,v)
  1295. end)
  1296. and _.all(b, function (i,v)
  1297. return _.include(a,v)
  1298. end)
  1299. end
  1300.  
  1301. --- Sorts a collection. If a comparison function is given, it will be used to sort objects
  1302. -- @name sort
  1303. -- @tparam table list a collection
  1304. -- @tparam[opt] function comp a comparison function prototyped as `comp(a,b)`, defaults to <tt><</tt> operator.
  1305. -- @treturn table the given list, sorted.
  1306. function _.sort(list,comp)
  1307. t_sort(list,comp)
  1308. return list
  1309. end
  1310.  
  1311. --- Converts a list of __var_args__ to a collection.
  1312. -- @name toArray
  1313. -- @tparam[opt] var_arg ... Optional variable number of arguments
  1314. -- @treturn table an array of all passed-in args
  1315. function _.toArray(...)
  1316. return {...}
  1317. end
  1318.  
  1319. --- Splits a collection into subsets. Each subset feature objects grouped by the result of passing it through an iterator.
  1320. -- If iterator is a string instead of a function, groups by the property named by iterator on each of the values.
  1321. -- @name groupBy
  1322. -- @tparam table list a collection
  1323. -- @tparam function iter an iterator function, prototyped as `iter(key,value,...)`
  1324. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `iter`
  1325. -- @treturn table a new collection with items grouped by subsets
  1326. function _.groupBy(list,iter,...)
  1327. local var_arg = {...}
  1328. local _list = {}
  1329. local _iter = _.isFunction(iter) and iter
  1330. or (_.isString(iter) and function(_,item)
  1331. return item[iter](item,unpack(var_arg))
  1332. end)
  1333. if not _iter then return end
  1334. _.each(list, function(i,v)
  1335. local _key = _iter(i,v)
  1336. if _list[_key] then _list[_key][#_list[_key]+1] = v
  1337. else _list[_key] = {v}
  1338. end
  1339. end)
  1340. return _list
  1341. end
  1342.  
  1343. --- Groups objects in a collection and counts them.
  1344. -- @name countBy
  1345. -- @tparam table list a collection
  1346. -- @tparam function iter an iterator function, prototyped as `iter(key,value,...)`
  1347. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `iter`
  1348. -- @treturn table a new collection with subsets names and count
  1349. function _.countBy(list,iter,...)
  1350. local var_arg = {...}
  1351. local stats = {}
  1352. _.each(list,function(i,v)
  1353. local key = iter(i,v,unpack(var_arg))
  1354. stats[key] = (stats[key] or 0) +1
  1355. end)
  1356. return stats
  1357. end
  1358.  
  1359. --- Counts the number of values in a collection. If being passed more than one args
  1360. -- it will return the number of passed-in args.
  1361. -- @name size
  1362. -- @tparam[opt] var_arg ... Optional variable number of arguments
  1363. -- @treturn number a size
  1364. function _.size(...)
  1365. local args = {...}
  1366. local arg1 = args[1]
  1367. if _.isNil(arg1) then
  1368. return 0
  1369. elseif _.isObject(arg1) then
  1370. return count(args[1])
  1371. else
  1372. return count(args)
  1373. end
  1374. end
  1375.  
  1376. --- Checks if all the keys of `other` object exists in a `list`. It does not
  1377. -- compares values. The test is not commutative, i.e `list` may contains keys
  1378. -- not existing in `other`.
  1379. -- @name containsKeys
  1380. -- @tparam table list a collection
  1381. -- @tparam table other a collection
  1382. -- @treturn boolean __true__ or __false__
  1383. function _.containsKeys(list,other)
  1384. for key in pairs(other) do
  1385. if not list[key] then return false end
  1386. end
  1387. return true
  1388. end
  1389.  
  1390. --- Checks if both given lists have the same keys. It does not compares values.
  1391. -- @name sameKeys
  1392. -- @tparam table listA a collection
  1393. -- @tparam table listB a collection
  1394. -- @treturn boolean __true__ or __false__
  1395. function _.sameKeys(listA,listB)
  1396. _.each(listA,function(key)
  1397. if not listB[key] then return false end
  1398. end)
  1399. _.each(listB,function(key)
  1400. if not listA[key] then return false end
  1401. end)
  1402. return true
  1403. end
  1404.  
  1405. -- ========= Array functions
  1406.  
  1407. --- Reverses values in a given array. The passed-in array should not be sparse.
  1408. -- @name reverse
  1409. -- @tparam table array an array
  1410. -- @treturn table a copy of the given array, reversed
  1411. function _.reverse(array)
  1412. local _array = {}
  1413. for i = #array,1,-1 do
  1414. _array[#_array+1] = array[i]
  1415. end
  1416. return _array
  1417. end
  1418.  
  1419. --- Collects values from a given array. The passed-in array should not be sparse.
  1420. -- This function collects values as long as they satisfy a given predicate.
  1421. -- Therefore, it returns on the first false test.
  1422. -- <br/><em>Aliased as @{takeWhile}</em>
  1423. -- @name selectWhile
  1424. -- @tparam table array an array
  1425. -- @tparam function f an iterator function prototyped as `f(key,value,...)`
  1426. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  1427. -- @treturn table a new table containing the first truthy values collected
  1428. -- @see takeWhile
  1429. function _.selectWhile(array,f,...)
  1430. local t = {}
  1431. for i,v in ipairs(array) do
  1432. if f(i,v,...) then t[i] = v else break end
  1433. end
  1434. return t
  1435. end
  1436.  
  1437. --- Alias for @{selectWhile}.
  1438. -- @function takeWhile
  1439. -- @param array
  1440. -- @param f
  1441. -- @param[opt] ...
  1442. _.takeWhile = _.selectWhile
  1443.  
  1444. --- Collects values from a given array. The passed-in array should not be sparse.
  1445. -- This function collects values as soon as they do not satisfy a given predicate.
  1446. -- Therefore it returns on the first true test.
  1447. -- <br/><em>Aliased as @{rejectWhile}</em>
  1448. -- @name dropWhile
  1449. -- @tparam table array an array
  1450. -- @tparam function f an iterator function prototyped as `f(key,value,...)`
  1451. -- @tparam[opt] var_arg ... Optional extra-args to be passed to function `f`
  1452. -- @treturn table a new table containing the first falsy values collected
  1453. -- @see rejectWhile
  1454. function _.dropWhile(array, f,...)
  1455. local _i
  1456. for i,v in ipairs(array) do
  1457. if not f(i,v,...) then
  1458. _i = i
  1459. break
  1460. end
  1461. end
  1462. if _.isNil(_i) then return {} end
  1463. return _.rest(array,_i)
  1464. end
  1465.  
  1466. --- Alias for @{dropWhile}.
  1467. -- @function rejectWhile
  1468. -- @param array
  1469. -- @param f
  1470. -- @param[opt] ...
  1471. _.rejectWhile = _.dropWhile
  1472.  
  1473. --- Returns the index at which a value should be inserted. This returned index is determined so that it maintains the sort.
  1474. -- If a comparison function is passed, it will be used to determine the sort ranking of each
  1475. -- value, including the passed-in value.
  1476. -- @name sortedIndex
  1477. -- @tparam table array an array
  1478. -- @tparam value the value to be inserted
  1479. -- @tparam[opt] function comp an comparison function prototyped as `f(a,b)`, defaults to <tt><</tt> operator
  1480. -- @tparam[optchain] boolean sort whether or not to sort the passed-in array
  1481. -- @treturn index the index at which the passed-in value should be inserted
  1482. function _.sortedIndex(array,value,comp,sort)
  1483. local _comp = comp or f_min
  1484. if sort then _.sort(array,_comp) end
  1485. for i = 1,#array do
  1486. if not _comp(array[i],value) then return i end
  1487. end
  1488. return #array+1
  1489. end
  1490.  
  1491. --- Returns the index of the given value in an array. If the passed-in value exists
  1492. -- more than once in the array, it will return the index of the first occurence.
  1493. -- @name indexOf
  1494. -- @tparam table array an array
  1495. -- @tparam value the value to be searched
  1496. -- @treturn index the index of the passed-in value or __nil__
  1497. function _.indexOf(array, value)
  1498. for k = 1,#array do
  1499. if array[k] == value then return k end
  1500. end
  1501. end
  1502.  
  1503. --- Returns the last occurence index of a given value.
  1504. -- @name lastIndexOf
  1505. -- @tparam table array an array
  1506. -- @tparam value the value to be searched
  1507. -- @treturn index the index of the last occurence of the passed-in value or __nil__
  1508. function _.lastIndexOf(array,value)
  1509. local key = _.indexOf(_.reverse(array),value)
  1510. if key then return #array-key+1 end
  1511. end
  1512.  
  1513. --- Adds all passed-in values at the top of an array. The latter args will come the
  1514. -- first in the given array.
  1515. -- @name add
  1516. -- @tparam table array an array
  1517. -- @tparam var_arg ... a variable number of arguments
  1518. -- @treturn table the passed-in array
  1519. function _.add(array,...)
  1520. _.each({...},function(i,v) t_insert(array,1,v) end)
  1521. return array
  1522. end
  1523.  
  1524. --- Pushes all passed-in values at the end of an array.
  1525. -- @name push
  1526. -- @tparam table array an array
  1527. -- @tparam var_arg ... a variable number of arguments
  1528. -- @treturn table the passed-in array
  1529. function _.push(array,...)
  1530. _.each({...}, function(i,v) array[#array+1] = v end)
  1531. return array
  1532. end
  1533.  
  1534. --- Removes and returns the value at the top of a given array.
  1535. -- <br/><em>Aliased as @{shift}</em>
  1536. -- @name pop
  1537. -- @tparam table array an array
  1538. -- @treturn value the popped value
  1539. -- @see shift
  1540. function _.pop(array)
  1541. local retValue = array[1]
  1542. t_remove(array,1)
  1543. return retValue
  1544. end
  1545.  
  1546. --- Alias for @{pop}.
  1547. -- @function shift
  1548. -- @param array
  1549. _.shift = _.pop
  1550.  
  1551. --- Removes and returns the value at the end of a given array.
  1552. -- @name unshift
  1553. -- @tparam table array an array
  1554. -- @treturn value the popped value
  1555. function _.unshift(array)
  1556. local retValue = array[#array]
  1557. t_remove(array)
  1558. return retValue
  1559. end
  1560.  
  1561. --- Trims all values indexed within the range `[start,finish]`.
  1562. -- <br/><em>Aliased as @{rmRange}</em>
  1563. -- @name removeRange
  1564. -- @tparam table array an array
  1565. -- @tparam[opt] index start the lower bound index, defaults to the first index in the array.
  1566. -- @tparam[optchain] index finish the upper bound index, defaults to the array length.
  1567. -- @treturn table the passed-in array
  1568. -- @see rmRange
  1569. function _.removeRange(array,start,finish)
  1570. local array = _.clone(array)
  1571. local i,n = (next(array)),#array
  1572. if n < 1 then return array end
  1573.  
  1574. start = clamp(start or i,i,n)
  1575. finish = clamp(finish or n,i,n)
  1576.  
  1577. if finish < start then return array end
  1578.  
  1579. local count = finish - start + 1
  1580. local i = start
  1581. while count > 0 do
  1582. t_remove(array,i)
  1583. count = count - 1
  1584. end
  1585. return array
  1586. end
  1587.  
  1588. --- Alias for @{removeRange}.
  1589. -- @function rmRange
  1590. -- @param array
  1591. -- @param[opt] start
  1592. -- @param[optchain] finish
  1593. _.rmRange = _.removeRange
  1594.  
  1595. --- Slices values indexed within `[start,finish]` range.
  1596. -- @name slice
  1597. -- @tparam table array an array
  1598. -- @tparam[opt] index start the lower bound index, defaults to the first index in the array.
  1599. -- @tparam[optchain] index finish the upper bound index, defaults to the array length.
  1600. -- @treturn table a new array
  1601. function _.slice(array,start,finish)
  1602. return _.select(array, function(index)
  1603. return (index >= (start or next(array)) and index <= (finish or #array))
  1604. end)
  1605. end
  1606.  
  1607. --- Returns the first N values in an array. <br/><em>Aliased as @{head}, @{take}</em>
  1608. -- @name first
  1609. -- @tparam table array an array
  1610. -- @tparam[opt] number n the number of values to be collected, defaults to 1.
  1611. -- @treturn table a new array
  1612. -- @see head
  1613. -- @see take
  1614. function _.first(array,n)
  1615. local n = n or 1
  1616. return _.slice(array,1, min(n,#array))
  1617. end
  1618.  
  1619. --- Alias for @{first}.
  1620. -- @function head
  1621. -- @param array
  1622. -- @param[opt] n
  1623. _.head = _.first
  1624.  
  1625. --- Alias for @{first}.
  1626. -- @function take
  1627. -- @param array
  1628. -- @param[opt] n
  1629. _.take = _.first
  1630.  
  1631. --- Returns all values in an array excluding the last N values.
  1632. -- @name initial
  1633. -- @tparam table array an array
  1634. -- @tparam[opt] number n the number of values to be left, defaults to the array length.
  1635. -- @treturn table a new array
  1636. function _.initial(array,n)
  1637. if n and n < 0 then return end
  1638. return _.slice(array,1, n and #array-(min(n,#array)) or #array-1)
  1639. end
  1640.  
  1641. --- Returns the last N values in an array.
  1642. -- @name last
  1643. -- @tparam table array an array
  1644. -- @tparam[opt] number n the number of values to be collected, defaults to the array length.
  1645. -- @treturn table a new array
  1646. function _.last(array,n)
  1647. if n and n <= 0 then return end
  1648. return _.slice(array,n and #array-min(n-1,#array-1) or 2,#array)
  1649. end
  1650.  
  1651. --- Trims all values before index. <br/><em>Aliased as @{tail}</em>
  1652. -- @name rest
  1653. -- @tparam table array an array
  1654. -- @tparam[opt] index index an index, defaults to 1
  1655. -- @treturn table a new array
  1656. -- @see tail
  1657. function _.rest(array,index)
  1658. if index and index > #array then return {} end
  1659. return _.slice(array,index and max(1,min(index,#array)) or 1,#array)
  1660. end
  1661.  
  1662. --- Alias for @{rest}.
  1663. -- @function tail
  1664. -- @param array
  1665. -- @param[opt] index
  1666. _.tail = _.rest
  1667.  
  1668. --- Trims all falsy values.
  1669. -- @name compact
  1670. -- @tparam table array an array
  1671. -- @treturn table a new array
  1672. function _.compact(array)
  1673. return _.reject(array, function (_,value)
  1674. return not value
  1675. end)
  1676. end
  1677.  
  1678. --- Flattens a nested array. Passing `shallow` will only flatten at the first single level.
  1679. -- @name flatten
  1680. -- @tparam table array an array
  1681. -- @tparam[opt] boolean shallow specifies the flattening depth
  1682. -- @treturn table a new array, flattened
  1683. function _.flatten(array, shallow)
  1684. local shallow = shallow or false
  1685. local new_flattened
  1686. local _flat = {}
  1687. for key,value in pairs(array) do
  1688. if _.isObject(value) and not shallow then
  1689. new_flattened = _.flatten (value)
  1690. _.each(new_flattened, function(_,item) _flat[#_flat+1] = item end)
  1691. else _flat[#_flat+1] = value
  1692. end
  1693. end
  1694. return _flat
  1695. end
  1696.  
  1697. --- Returns values from an array not present in all passed-in args. <br/><em>Aliased as @{without}</em>
  1698. -- @name difference
  1699. -- @tparam table array an array
  1700. -- @tparam var_arg ... a variable number of arguments
  1701. -- @treturn table a new array
  1702. -- @see without
  1703. function _.difference(array,...)
  1704. local values = _.toArray(...)
  1705. return _.select(array,function(i,value)
  1706. return not _.include(values,value)
  1707. end)
  1708. end
  1709.  
  1710. --- Alias for @{difference}.
  1711. -- @function without
  1712. -- @param array
  1713. -- @param ...
  1714. _.without = _.difference
  1715.  
  1716. --- Produces a duplicate-free version of a given array. Passing `isSorted` will
  1717. -- provide a much faster version, but will produce the desired result if the array
  1718. -- is properly sorted. If `iter` is passed, it will be used to compute new values based on
  1719. -- a transformation.
  1720. -- <br/><em>Aliased as @{unique}</em>
  1721. -- @name uniq
  1722. -- @tparam table array an array
  1723. -- @tparam[opt] boolean isSorted whether or not the passed-in array is already sorted, defaults to `false`
  1724. -- @tparam[optchain] function iter an iterator function prototyped as `iter(key,value,...)`
  1725. -- @tparam[optchain] var_arg ... Optional extra-args to be passed to `iter` function
  1726. -- @treturn table a new array
  1727. -- @see unique
  1728. function _.uniq(array,isSorted,iter,...)
  1729. local init = iter and _.map(array,iter,...) or array
  1730. local result = {}
  1731. if not isSorted then
  1732. for __,v in ipairs(init) do
  1733. if not _.include(result,v) then
  1734. result[#result+1] = v
  1735. end
  1736. end
  1737. return result
  1738. end
  1739.  
  1740. result[#result+1] = init[1]
  1741. for i = 2,#init do
  1742. if init[i] ~= result[#result] then
  1743. result[#result+1] = init[i]
  1744. end
  1745. end
  1746. return result
  1747. end
  1748.  
  1749. --- Alias for @{uniq}.
  1750. -- @function unique
  1751. -- @param array
  1752. -- @param[opt] isSorted
  1753. -- @param[optchain] iter
  1754. -- @param[optchain] ...
  1755. _.unique = _.uniq
  1756.  
  1757. --- Returns the duplicate-free union of all passed in arrays.
  1758. -- @name union
  1759. -- @tparam var_arg ... a variable number of arrays arguments
  1760. -- @treturn table a new array
  1761. function _.union(...)
  1762. return _.uniq(_.flatten({...}))
  1763. end
  1764.  
  1765. --- Returns the intersection of all passed-in arrays.
  1766. -- Each value in the result is present in each of the passed-in arrays.
  1767. -- @name intersection
  1768. -- @tparam table array an array
  1769. -- @tparam var_arg ... a variable number of array arguments
  1770. -- @treturn table a new array
  1771. function _.intersection(array,...)
  1772. local arg = {...}
  1773. local _intersect = {}
  1774. for i,value in ipairs(array) do
  1775. if _.all(arg,function(i,v)
  1776. return _.include(v,value)
  1777. end) then
  1778. t_insert(_intersect,value)
  1779. end
  1780. end
  1781. return _intersect
  1782. end
  1783.  
  1784. --- Merges values of each of the passed-in arrays in subsets.
  1785. -- Only values at the same position in the initial array are merged.
  1786. -- @name zip
  1787. -- @tparam var_arg ... a variable number of array arguments
  1788. -- @treturn table a new array
  1789. function _.zip(...)
  1790. local arg = {...}
  1791. local _len = _.max(_.map(arg,function(i,v)
  1792. return #v
  1793. end))
  1794. local _ans = {}
  1795. for i = 1,_len do
  1796. _ans[i] = _.pluck(arg,i)
  1797. end
  1798. return _ans
  1799. end
  1800.  
  1801. --- Clones `array` and appends `other` to the result.
  1802. -- @name append
  1803. -- @tparam table array an array
  1804. -- @tparam table other an array
  1805. -- @treturn table a new array
  1806. function _.append(array,other)
  1807. local t = {}
  1808. for i,v in ipairs(array) do t[i] = v end
  1809. for i,v in ipairs(other) do t[#t+1] = v end
  1810. return t
  1811. end
  1812.  
  1813. --- Produce a flexible list of numbers. If one positive value is passed, will count from 0 to that value,
  1814. -- with a default step of 1. If two values were passed, will count from the first one to the second one, with the
  1815. -- same default step of 1. A third passed value will be considered a step value.
  1816. -- <br/><em>Aliased as @{count}</em>
  1817. -- @name range
  1818. -- @tparam var_arg ... a variable list of numbers
  1819. -- @treturn table a new array of numbers
  1820. -- @see count
  1821. function _.range(...)
  1822. local arg = {...}
  1823. local _start,_stop,_step
  1824. if #arg==0 then return {}
  1825. elseif #arg==1 then _stop,_start,_step = arg[1],0,1
  1826. elseif #arg==2 then _start,_stop,_step = arg[1],arg[2],1
  1827. elseif #arg == 3 then _start,_stop,_step = arg[1],arg[2],arg[3]
  1828. end
  1829. if (_step and _step==0) then return {} end
  1830. local _ranged = {}
  1831. local _steps = max(floor((_stop-_start)/_step),0)
  1832. for i=1,_steps do _ranged[#_ranged+1] = _start+_step*i end
  1833. if #_ranged>0 then t_insert(_ranged,1,_start) end
  1834. return _ranged
  1835. end
  1836.  
  1837. --- Alias for @{range}.
  1838. -- @function count
  1839. -- @param ...
  1840. _.count = _.range
  1841.  
  1842. --- Inverts `key-value` pairs. Keys becomes values, while values becomes keys.
  1843. -- <br/><em>Aliased as @{mirror}</em>
  1844. -- @name invert
  1845. -- @tparam table array a given array
  1846. -- @treturn table a new array
  1847. -- @see mirror
  1848. function _.invert(array)
  1849. local _ret = {}
  1850. _.each(array,function(i,v) _ret[v] = i end)
  1851. return _ret
  1852. end
  1853.  
  1854. --- Alias for @{invert}.
  1855. -- @function mirror
  1856. -- @param array
  1857. _.mirror = _.invert
  1858.  
  1859. --- Concatenates values in a given array. Handles booleans as well. If `sep` string is
  1860. -- passed, it will be used as a separator. Optional `i` and `j` will only concatenate
  1861. -- values within `[i,j]` range. <br/><em>Aliased as @{join}</em>
  1862. -- @name concat
  1863. -- @tparam table array a given array
  1864. -- @tparam[opt] string sep a separator string, defaults to `''`.
  1865. -- @tparam[optchain] number i the starting index, defaults to 1.
  1866. -- @tparam[optchain] number j the final index, defaults to the array length.
  1867. -- @treturn string a string
  1868. -- @see join
  1869. function _.concat(array,sep,i,j)
  1870. local _array = _.map(array,function(i,v)
  1871. return tostring(v)
  1872. end)
  1873. return t_concat(_array,sep,i,j)
  1874.  
  1875. end
  1876.  
  1877. --- Alias for @{concat}.
  1878. -- @function join
  1879. -- @param array
  1880. -- @param[opt] sep
  1881. -- @param[optchain] i
  1882. -- @param[optchain] j
  1883. _.join = _.concat
  1884.  
  1885. -- ========= Utility functions
  1886.  
  1887. --- Returns the passed-in value. Used internally as a default iterator.
  1888. -- @name identity
  1889. -- @tparam arg value a value
  1890. -- @treturn arg the passed-in value
  1891. function _.identity(...)
  1892. return ...
  1893. end
  1894.  
  1895. --- Returns a version of `f` that runs only once. Successive calls will make `f`
  1896. -- keep yielding the same answer, no matter what the passed-in arguments are. Can be used to
  1897. -- init variables as well.
  1898. -- @name once
  1899. -- @tparam function f a function
  1900. -- @treturn function a new function
  1901. function _.once(f)
  1902. local _internal = 0
  1903. local _args = {}
  1904. return function(...)
  1905. _internal = _internal+1
  1906. if _internal<=1 then _args = {...} end
  1907. return f(unpack(_args))
  1908. end
  1909. end
  1910.  
  1911. --- Memoizes a given function by caching the computed result.
  1912. -- Useful for speeding up slow-running functions. If function `hash` is passed,
  1913. -- it will be used to compute unique keys from args passed to `hash` for results caching.
  1914. -- <br/><em>Aliased as @{cache}</em>
  1915. -- @name memoize
  1916. -- @tparam function f a function
  1917. -- @tparam[opt] function hash a hash function, defaults to @{identity}
  1918. -- @treturn function a new function
  1919. -- @see cache
  1920. function _.memoize(f,hash)
  1921. local _cache = setmetatable({},{__mode = 'kv'})
  1922. local _hasher = hash or _.identity
  1923. return function (...)
  1924. local _hashKey = _hasher(...)
  1925. local _result = _cache[_hashKey]
  1926. if not _result then _cache[_hashKey] = f(...) end
  1927. return _cache[_hashKey]
  1928. end
  1929. end
  1930.  
  1931. --- Alias for @{memoize}.
  1932. -- @function cache
  1933. -- @param f
  1934. -- @param[opt] hash
  1935. _.cache = _.memoize
  1936.  
  1937. --- Returns a version of `f` that runs on the `count-th` call.
  1938. -- Useful when dealing with asynchronous tasks.
  1939. -- @name after
  1940. -- @tparam function f a function
  1941. -- @tparam number count the number of calls before `f` answers
  1942. -- @treturn function a new function
  1943. function _.after(f,count)
  1944. local _limit,_internal = count, 0
  1945. return function(...)
  1946. _internal = _internal+1
  1947. if _internal >= _limit then return f(...) end
  1948. end
  1949. end
  1950.  
  1951. --- Composes functions. Each passed-in function consumes the return value of the function that follows.
  1952. -- In math terms, composing the functions `f`, `g`, and `h` produces `f(g(h(...)))`.
  1953. -- @name compose
  1954. -- @tparam var_arg ... a variable number of functions
  1955. -- @treturn function a new function
  1956. function _.compose(...)
  1957. local f = _.reverse {...}
  1958. return function (...)
  1959. local _temp
  1960. for i,func in pairs(f) do
  1961. _temp = _temp and func(_temp) or func(...)
  1962. end
  1963. return _temp
  1964. end
  1965. end
  1966.  
  1967. --- Wraps `f` inside of the `wrapper` function. It passes `f` as the first argument to `wrapper`.
  1968. -- This allows the wrapper to execute code before and after `f` runs,
  1969. -- adjust the arguments, and execute it conditionally.
  1970. -- @name wrap
  1971. -- @tparam function f a function to be wrapped, prototyped as `f(...)`
  1972. -- @tparam function wrapper a wrapper function, prototyped as `wrapper(f,...)`
  1973. -- @treturn function a new function
  1974. function _.wrap(f,wrapper)
  1975. return function (...)
  1976. return wrapper(f,...)
  1977. end
  1978. end
  1979.  
  1980. --- Runs `iter` function `n` times.
  1981. -- Collects the results of each run and returns them in an array.
  1982. -- @name times
  1983. -- @tparam number n the number of times `iter` should be called
  1984. -- @tparam function iter an iterator function, prototyped as `iter(i,...)`
  1985. -- @tparam var_arg ... extra-args to be passed to `iter` function
  1986. -- @treturn table an array of results
  1987. function _.times(n,iter,...)
  1988. local results = {}
  1989. for i = 1,n do
  1990. results[i] = iter(i,...)
  1991. end
  1992. return results
  1993. end
  1994.  
  1995. --- Binds `v` to be the first argument to function `f`. As a result,
  1996. -- calling `f(...)` will result to `f(v,...)`.
  1997. -- @name bind
  1998. -- @tparam function f a function
  1999. -- @tparam arg v an argument
  2000. -- @treturn function a function, prototyped as `f(v,...)`
  2001. function _.bind(f,v)
  2002. return function (...)
  2003. return f(v,...)
  2004. end
  2005. end
  2006.  
  2007. --- Binds `...` to be the N-first arguments to function `f`. As a result,
  2008. -- calling `f(a1,a2,...,aN)` will result to `f(...,a1,a2,...,aN)`.
  2009. -- @name bindn
  2010. -- @tparam function f a function
  2011. -- @tparam var_arg ... a variable numer of arguments
  2012. -- @treturn function a function, prototyped as `f(v,...)`
  2013. function _.bindn(f,...)
  2014. local iArg = {...}
  2015. return function (...)
  2016. return f(unpack(_.append(iArg,{...})))
  2017. end
  2018. end
  2019.  
  2020. --- Generates a unique Id (unique for the current session). If given a sring `template`
  2021. -- will use this template for output formatting. Otherwise, if `template` is a function,
  2022. -- will compute an output running `template(id,...)`.
  2023. -- <br/><em>Aliased as @{uId}</em>.
  2024. -- @name uniqueId
  2025. -- @tparam[opt] string|function template either a string or a function
  2026. -- @tparam[optchain] var_arg ... a variable numer of arguments to be passed to `template`, in case it is a function.
  2027. -- @treturn id a formatted Id
  2028. -- @see uId
  2029. function _.uniqueId(template,...)
  2030. unique_id_counter = unique_id_counter + 1
  2031. if template then
  2032. if _.isString(template) then
  2033. return template:format(unique_id_counter)
  2034. elseif _.isFunction(template) then
  2035. return template(unique_id_counter,...)
  2036. end
  2037. end
  2038. return unique_id_counter
  2039. end
  2040.  
  2041. --- Alias for @{uniqueId}.
  2042. -- @function uId
  2043. -- @param[opt] template
  2044. -- @param[optchain] ...
  2045. _.uId = _.uniqueId
  2046.  
  2047. -- ========= Object functions
  2048.  
  2049. --- Returns the names of the object properties.
  2050. -- @name keys
  2051. -- @tparam table obj an object
  2052. -- @treturn table an array
  2053. function _.keys(obj)
  2054. local _oKeys = {}
  2055. _.each(obj,function(key,_) _oKeys[#_oKeys+1]=key end)
  2056. return _oKeys
  2057. end
  2058.  
  2059. --- Returns the values of the object properties.
  2060. -- @name values
  2061. -- @tparam table obj an object
  2062. -- @treturn table an array
  2063. function _.values(obj)
  2064. local _oValues = {}
  2065. _.each(obj,function(_,value) _oValues[#_oValues+1]=value end)
  2066. return _oValues
  2067. end
  2068.  
  2069. --- Returns an array of `obj` key-values pairs
  2070. -- @name pairs
  2071. -- @tparam table obj an object
  2072. -- @treturn table an array
  2073. function _.pairs(obj)
  2074. local paired= {}
  2075. _.each(obj,function(k,v)
  2076. paired[#paired+1] = {k,v}
  2077. end)
  2078. return paired
  2079. end
  2080.  
  2081. --- Extends an object properties. It copies all of the properties of extra passed-in objects
  2082. -- over to the destination object, and returns the destination object.
  2083. -- The last object in `...` will override properties of the same name in the previous one
  2084. -- @name extend
  2085. -- @tparam table destObj a destination object
  2086. -- @tparam var_arg ... a variable number of array arguments
  2087. -- @treturn table the destination object extended
  2088. function _.extend(destObj,...)
  2089. local sources = {...}
  2090. _.each(sources,function(__,source)
  2091. if _.isObject(source) then
  2092. _.each(source,function(key,value)
  2093. destObj[key] = value
  2094. end)
  2095. end
  2096. end)
  2097. return destObj
  2098. end
  2099.  
  2100. --- Returns a sorted list of all methods names found in an object. If the given object
  2101. -- has a metatable implementing an `__index` field pointing to another table, will also recurse on this
  2102. -- table. If `output` is provided, will export results in this table.
  2103. -- <br/><em>Aliased as @{methods}</em>.
  2104. -- @name functions
  2105. -- @tparam table obj an object
  2106. -- @tparam[opt] table output Optional table to collect the results
  2107. -- @treturn table an array of methods names
  2108. -- @see methods
  2109. function _.functions(obj,output)
  2110. if not obj then return _.sort(_.keys(_)) end
  2111. local _methods = output or {}
  2112. _.each(obj,function(key,value)
  2113. if _.isFunction(value) then
  2114. _methods[#_methods+1]=key
  2115. end
  2116. end)
  2117. local mt = getmetatable(obj)
  2118. if mt and mt.__index then
  2119. _.functions(mt.__index,_methods)
  2120. end
  2121. return _.sort(_methods)
  2122. end
  2123.  
  2124. --- Alias for @{functions}.
  2125. -- @function methods
  2126. -- @param obj
  2127. -- @param[opt] output
  2128. _.methods = _.functions
  2129.  
  2130. --- Clones a given object properties. If `shallow` is passed
  2131. -- will also clone nested array properties.
  2132. -- @name clone
  2133. -- @tparam table obj an object
  2134. -- @tparam[opt] boolean shallow whether or not nested array-properties should be cloned, defaults to false.
  2135. -- @treturn table a clone of the passed-in object
  2136. function _.clone(obj,shallow)
  2137. if not _.isObject(obj) then return obj end
  2138. local _obj = {}
  2139. _.each(obj,function(i,v)
  2140. if _.isObject(v) then
  2141. if not shallow then
  2142. _obj[i] = _.clone(v,shallow)
  2143. else _obj[i] = v
  2144. end
  2145. else
  2146. _obj[i] = v
  2147. end
  2148. end)
  2149. return _obj
  2150. end
  2151.  
  2152. --- Checks if a given object implements a property.
  2153. -- @name has
  2154. -- @tparam table obj an object
  2155. -- @tparam key key a key property to be checked
  2156. -- @treturn boolean __true__ or __false__
  2157. function _.has(obj, key)
  2158. return obj[key]~=nil
  2159. end
  2160.  
  2161. --- Return a filtered copy of the object. The returned object will only have
  2162. -- the whitelisted properties. <br/><em>Aliased as @{choose}</em>.
  2163. -- @name pick
  2164. -- @tparam table obj an object
  2165. -- @tparam var_arg ... a variable number of string keys
  2166. -- @treturn table the filtered object
  2167. -- @see choose
  2168. function _.pick(obj, ...)
  2169. local whitelist = _.flatten {...}
  2170. local _picked = {}
  2171. _.each(whitelist,function(key,property)
  2172. if obj[property] then
  2173. _picked[property] = obj[property]
  2174. end
  2175. end)
  2176. return _picked
  2177. end
  2178.  
  2179. --- Alias for @{pick}.
  2180. -- @function choose
  2181. -- @param obj
  2182. -- @param ...
  2183. _.choose = _.pick
  2184.  
  2185. --- Return a filtered copy of the object. The returned object will not have
  2186. -- the blacklisted properties. <br/><em>Aliased as @{drop}</em>.
  2187. -- @name omit
  2188. -- @tparam table obj an object
  2189. -- @tparam var_arg ... a variable number of string keys
  2190. -- @treturn table the filtered object
  2191. -- @see drop
  2192. function _.omit(obj,...)
  2193. local blacklist = _.flatten {...}
  2194. local _picked = {}
  2195. _.each(obj,function(key,value)
  2196. if not _.include(blacklist,key) then
  2197. _picked[key] = value
  2198. end
  2199. end)
  2200. return _picked
  2201. end
  2202.  
  2203. --- Alias for @{omit}.
  2204. -- @function drop
  2205. -- @param obj
  2206. -- @param ...
  2207. _.drop = _.omit
  2208.  
  2209. --- Fills nil properties in an object with the given `template` object. Pre-existing
  2210. -- properties will be preserved. <br/><em>Aliased as @{defaults}</em>.
  2211. -- @name template
  2212. -- @tparam table obj an object
  2213. -- @tparam table template a template object
  2214. -- @treturn table the passed-in object filled
  2215. -- @see defaults
  2216. function _.template(obj,template)
  2217. _.each(template,function(i,v)
  2218. if not obj[i] then
  2219. obj[i] = v
  2220. end
  2221. end)
  2222. return obj
  2223. end
  2224.  
  2225. --- Alias for @{template}.
  2226. -- @function defaults
  2227. -- @param obj
  2228. -- @param template
  2229. _.defaults = _.template
  2230.  
  2231. --- Performs an equality test between two objects.
  2232. -- Can compare strings, functions (by reference), nil, booleans. Compares tables by reference or by component-values.
  2233. -- Comparing objects, if `useMt` is passed, the equality operator `==` will be used if one of
  2234. -- the given objects has a metatable implementing `mt.__eq`
  2235. -- @name isEqual
  2236. -- @tparam table objA an object
  2237. -- @tparam table objB another object
  2238. -- @tparam[opt] boolean useMt whether or not `mt.__eq` should be used, defaults to false.
  2239. -- @treturn boolean __true__ or __false__
  2240. function _.isEqual(objA,objB,useMt)
  2241. local typeObjA = type(objA)
  2242. local typeObjB = type(objB)
  2243. if typeObjA~=typeObjB then return false end
  2244. if typeObjA~='table' then return (objA==objB) end
  2245.  
  2246. local mtA = getmetatable(objA)
  2247. local mtB = getmetatable(objB)
  2248.  
  2249. if useMt then
  2250. if mtA or mtB and mtA.__eq or mtB.__eq then
  2251. return (objA==objB)
  2252. end
  2253. end
  2254.  
  2255. if _.size(objA)~=_.size(objB) then return false end
  2256.  
  2257. for i,v1 in pairs(objA) do
  2258. local v2 = objB[i]
  2259. if _.isNil(v2) or not _.isEqual(v1,v2,useMt) then return false end
  2260. end
  2261.  
  2262. for i,v1 in pairs(objB) do
  2263. local v2 = objA[i]
  2264. if _.isNil(v2) then return false end
  2265. end
  2266.  
  2267. return true
  2268. end
  2269.  
  2270. --- Invokes an object method. It passes the object itself as the first argument. if `method` is not
  2271. -- callable, will return `obj[method]`.
  2272. -- @name result
  2273. -- @tparam table obj an object
  2274. -- @tparam string method a string key to index in object `obj`.
  2275. -- @tparam[opt] var_arg ... Optional extra-args to be passed to `method`
  2276. -- @treturn value the returned value of `method(obj,...)` call
  2277. function _.result(obj,method,...)
  2278. if obj[method] then
  2279. if _.isCallable(obj[method]) then
  2280. return obj[method](obj,...)
  2281. else return obj[method]
  2282. end
  2283. end
  2284. if _.isCallable(method) then
  2285. return method(obj,...)
  2286. end
  2287. end
  2288.  
  2289. --- Checks if the given arg is an object (i.e a Lua table).
  2290. -- @name isObject
  2291. -- @tparam object obj an object
  2292. -- @treturn boolean __true__ or __false__
  2293. function _.isObject(obj)
  2294. return type(obj) == 'table'
  2295. end
  2296.  
  2297. --- Checks if the given arg is an callable. Assumes `obj` is callable if
  2298. -- it is either a function or a table having a metatable implementing `__call` metamethod.
  2299. -- @name isCallable
  2300. -- @tparam object obj an object
  2301. -- @treturn boolean __true__ or __false__
  2302. function _.isCallable(obj)
  2303. return (_.isFunction(obj) or
  2304. (_.isObject(obj) and getmetatable(obj)
  2305. and getmetatable(obj).__call~=nil) or false)
  2306. end
  2307.  
  2308. --- Checks if the given arg is an array. Assumes `obj` is an array
  2309. -- if is a table with integer numbers as indexes.
  2310. -- @name isArray
  2311. -- @tparam object obj an object
  2312. -- @treturn boolean __true__ or __false__
  2313. function _.isArray(obj)
  2314. if not _.isObject(obj) then return false end
  2315. return _.all(_.keys(obj),function(i,v)
  2316. return _.isNumber(v) and (floor(v)==v)
  2317. end)
  2318. end
  2319.  
  2320. --- Checks if the given is empty. If `obj` is a @{string}, will return __true__
  2321. -- if `#obj==0`. Otherwise, if `obj` is a table, will return whether or not this table
  2322. -- is empty.
  2323. -- @name isEmpty
  2324. -- @tparam object obj an object
  2325. -- @treturn boolean __true__ or __false__
  2326. function _.isEmpty(obj)
  2327. if _.isString(obj) then return #obj==0 end
  2328. if _.isObject(obj) then return next(obj)==nil end
  2329. return true
  2330. end
  2331.  
  2332. --- Checks if the given arg is a @{string}.
  2333. -- @name isString
  2334. -- @tparam object obj an object
  2335. -- @treturn boolean __true__ or __false__
  2336. function _.isString(obj)
  2337. return type(obj) == 'string'
  2338. end
  2339.  
  2340. --- Checks if the given arg is a function.
  2341. -- @name isFunction
  2342. -- @tparam object obj an object
  2343. -- @treturn boolean __true__ or __false__
  2344. function _.isFunction(obj)
  2345. return type(obj) == 'function'
  2346. end
  2347.  
  2348. --- Checks if the given arg is nil.
  2349. -- @name isNil
  2350. -- @tparam object obj an object
  2351. -- @treturn boolean __true__ or __false__
  2352. function _.isNil(obj)
  2353. return obj==nil
  2354. end
  2355.  
  2356. --- Checks if the given arg is a finite number.
  2357. -- @name isFinite
  2358. -- @tparam object obj a number
  2359. -- @treturn boolean __true__ or __false__
  2360. function _.isFinite(obj)
  2361. if not _.isNumber(obj) then return false end
  2362. return obj > -huge and obj < huge
  2363. end
  2364.  
  2365. --- Checks if the given arg is a number.
  2366. -- @name isNumber
  2367. -- @tparam object obj a number
  2368. -- @treturn boolean __true__ or __false__
  2369. function _.isNumber(obj)
  2370. return type(obj) == 'number'
  2371. end
  2372.  
  2373. --- Checks if the given arg is NaN (see [Not-A-Number](http://en.wikipedia.org/wiki/NaN)).
  2374. -- @name isNaN
  2375. -- @tparam object obj a number
  2376. -- @treturn boolean __true__ or __false__
  2377. function _.isNaN(obj)
  2378. return _.isNumber(obj) and obj~=obj
  2379. end
  2380.  
  2381. --- Checks if the given arg is a boolean.
  2382. -- @name isBoolean
  2383. -- @tparam object obj a boolean
  2384. -- @treturn boolean __true__ or __false__
  2385. function _.isBoolean(obj)
  2386. return type(obj) == 'boolean'
  2387. end
  2388.  
  2389. --- Imports all library functions in the global environment. <br/><em>Aliased as @{mixin}</em>
  2390. -- @name import
  2391. -- @see mixin
  2392. local function import()
  2393. local fn = _.functions()
  2394. local env = getfenv()
  2395. _.each(fn,function(i,fName)
  2396. env[fName] = _[fName]
  2397. end)
  2398. end
  2399.  
  2400. --- Alias for @{import}.
  2401. -- @function mixin
  2402. local mixin = import
  2403.  
  2404. local _mt = {import = import, mixin = mixin}
  2405. _mt.__index = _mt
  2406. return setmetatable (_, _mt)]===================],"CoolisOS/bin/kernel/checker",true,"CoolisOS/bin/kernel/checker/init.lua",[===================[local classname=require'kernel.class'.classname
  2407. custom={}
  2408.  
  2409. function conforms(t,a)
  2410. local k=t:sub(1, 1)
  2411. return t == "?"
  2412. or (k == "?" and (a==nil or conforms(t:sub(2, -1),a)))
  2413. or (k == "!" and not (conforms(t:sub(2, -1),a)))
  2414. or type(a)==t
  2415. or classname(a) == t
  2416. or (custom[t] and custom[t](a))
  2417. end
  2418.  
  2419. function check_one(d,a)
  2420. for t in d:gmatch('|?([^|]+)|?') do
  2421. if conforms(t,a) then return true end
  2422. end
  2423. return false
  2424. end
  2425. function check(s,...)
  2426. if type(s)~='string' then error('arg1 is not of type string',2) end
  2427. local i=0
  2428. for typ in s:gmatch(',?([^,]+),?') do
  2429. i=i+1
  2430. if not check_one(typ,select(i,...)) then
  2431. local msg=string.format(
  2432. 'arg%d:(%s) is not of type (%s);\n args=%s',
  2433. i,tostring((debug and debug.getinfo or type)((select(i,...)))),typ,
  2434. (debug.args_tostring(...)))
  2435. error(msg,3)
  2436. end
  2437. end
  2438. end]===================],"CoolisOS/bin/kernel/class",true,"CoolisOS/bin/kernel/class/Box.lua",[===================[include'kernel.class'
  2439. local Opt=require'kernel.class.Opt'
  2440. local compat=require'kernel.compat'
  2441. local check=require'kernel.checker'.check
  2442. local pipe=sched.pipe
  2443. class.Box()
  2444. Box.token={}
  2445. do
  2446. local def=Opt{
  2447. pin=Opt(pipe),
  2448. pout=Opt(pipe),
  2449. perr=Opt(pipe),
  2450. cc=false,
  2451. terminal=Opt(nil),
  2452. env=Opt(function()
  2453. local env=util.shcopy(_G)
  2454. env._G=env
  2455. return env
  2456. end)
  2457. }
  2458. function Box:__init(opt)
  2459. def:drop(self,opt)
  2460. self.env[Box.token]=self
  2461. self.env._BOX=self
  2462. if self.terminal then
  2463. self.env.write=function(...)
  2464. return self.terminal:write(...)
  2465. end
  2466. self.env.read=function(...)
  2467. return self.terminal:read(...)
  2468. end
  2469. self.env.printError=function(s)
  2470. return debug.printError(s,self.terminal)
  2471. end
  2472. self.env.term=self.terminal.term
  2473. else
  2474. self.env.print=function(s)
  2475. self.pout:send(s)
  2476. end
  2477. self.env.write=function(s)
  2478. self.pout:send(s)
  2479. end
  2480. self.env.read=function()
  2481. self.pin:receive()
  2482. end
  2483. self.env.printError=function(s)
  2484. self.perr:send(s)
  2485. end
  2486. end
  2487. self.env.os=util.shcopy(self.env.os)
  2488. self.env.os.execute=function(s)
  2489. if not self.env.shell then error'no shell' end
  2490. return self.env.shell.execute(s)
  2491. end
  2492. end
  2493. function Box:run(f,...)
  2494. setfenv(f,self.env)
  2495. return f(...)
  2496. end
  2497. Box.get_current=function(level)
  2498. level=(level or 0)
  2499. repeat
  2500. level=level+1
  2501. local env=getfenv(level)
  2502. if env[Box.token] then
  2503. return env[Box.token]
  2504. end
  2505. until env==_Gstore.base_env
  2506. end
  2507.  
  2508. end
  2509.  
  2510. return Box]===================],"CoolisOS/bin/kernel/class/init.lua",[===================[-- classlib.lua 2.04.04
  2511. --
  2512. -- Changes from 2.03:
  2513. --
  2514. -- 1. Included patches and ideas from Peter Schaefer
  2515. -- ([email protected]) considerably improving the efficiency of
  2516. -- build(), __init() and other parts of the code.
  2517. --
  2518. -- 2. The former remove_ambiguous() function was removed, improving the
  2519. -- efficiency of both build() (instance creation) and mt:__call() (class
  2520. -- creation). Ambiguities are now processed using the ambiguous_keys
  2521. -- tables introduced by Peter.
  2522. --
  2523. -- 3. Removed inheritance of class properties, which was inconsistent with
  2524. -- the way instance properties are handled (see pages 4-5 of the manual).
  2525. -- Mostly harmless, but confusing. Now only methods are inherited.
  2526. --
  2527.  
  2528. -- PRIVATE
  2529.  
  2530. --[[
  2531. Define unique value for identifying ambiguous base objects and inherited
  2532. attributes. Ambiguous values are normally removed from classes and objects,
  2533. but if keep_ambiguous == true they are left there and the ambiguous value
  2534. is made to behave in a way useful for debugging.
  2535. ]]
  2536.  
  2537. --[[
  2538. Changes for OS:
  2539. -__root field of each self points to the deepest self table;
  2540. -methods matching '^_[^_]' are not copied into subclasses;
  2541.  
  2542. ]]
  2543.  
  2544.  
  2545. local ambiguous
  2546.  
  2547. if keep_ambiguous then
  2548.  
  2549. ambiguous = { _type = 'ambiguous' }
  2550.  
  2551. local function invalid(operation)
  2552. return function()
  2553. error('Invalid ' .. operation .. ' on ambiguous')
  2554. end
  2555. end
  2556.  
  2557. -- Make ambiguous complain about everything except tostring()
  2558. local ambiguous_mt =
  2559. {
  2560. __add = invalid('addition'),
  2561. __sub = invalid('substraction'),
  2562. __mul = invalid('multiplication'),
  2563. __div = invalid('division'),
  2564. __mod = invalid('modulus operation'),
  2565. __pow = invalid('exponentiation'),
  2566. __unm = invalid('unary minus'),
  2567. __concat = invalid('concatenation'),
  2568. __len = invalid('length operation'),
  2569. __eq = invalid('equality comparison'),
  2570. __lt = invalid('less than'),
  2571. __le = invalid('less or equal'),
  2572. __index = invalid('indexing'),
  2573. __newindex = invalid('new indexing'),
  2574. __call = invalid('call'),
  2575. __tostring = function() return 'ambiguous' end,
  2576. __tonumber = invalid('conversion to number')
  2577. }
  2578. setmetatable(ambiguous, ambiguous_mt)
  2579.  
  2580. end
  2581.  
  2582.  
  2583. --[[
  2584. Reserved attribute names.
  2585. ]]
  2586.  
  2587. local reserved =
  2588. {
  2589. __index = true,
  2590. __newindex = true,
  2591. __type = true,
  2592. __class = true,
  2593. __bases = true,
  2594. __inherited = true,
  2595. __from = true,
  2596. __shared = true,
  2597. __user_init = true,
  2598. __name = true,
  2599. __initialized = true,
  2600. __root = true,
  2601. }
  2602.  
  2603. --[[
  2604. Some special user-set attributes are renamed.
  2605. ]]
  2606.  
  2607. local rename =
  2608. {
  2609. __init = '__user_init',
  2610. __set = '__user_set',
  2611. __get = '__user_get'
  2612. }
  2613.  
  2614. --[[
  2615. The metatable of all classes, containing:
  2616.  
  2617. To be used by the classes:
  2618. __call() for creating instances
  2619. __init() default constructor
  2620. is_a() for checking object and class types
  2621. implements() for checking interface support
  2622.  
  2623. For internal use:
  2624. __newindex() for controlling class population
  2625. ]]
  2626.  
  2627. local class_mt = {}
  2628. class_mt.__index = class_mt
  2629.  
  2630. --[[
  2631. This controls class population.
  2632. Here 'self' is a class being populated by inheritance or by the user.
  2633. ]]
  2634.  
  2635. function class_mt:__newindex(name, value)
  2636.  
  2637. -- Rename special user-set attributes
  2638. if rename[name] then name = rename[name] end
  2639.  
  2640. -- __user_get() needs an __index() handler
  2641. if name == '__user_get' then
  2642. self.__index = value and function(obj, k)
  2643. local v = self[k]
  2644. if v == nil and not reserved[k] then v = value(obj, k) end
  2645. return v
  2646. end or self
  2647.  
  2648. -- __user_set() needs a __newindex() handler
  2649. elseif name == '__user_set' then
  2650. self.__newindex = value and function(obj, k, v)
  2651. if reserved[k] or not value(obj, k, v) then rawset(obj, k, v) end
  2652. end or nil
  2653.  
  2654. end
  2655.  
  2656. -- Assign the attribute
  2657. rawset(self, name, value)
  2658. end
  2659.  
  2660. --[[
  2661. This function creates an object of a certain class and calls itself
  2662. recursively to create one child object for each base class. Base objects
  2663. of unnamed base classes are accessed by using the base class as an index
  2664. into the object, base objects of named base classes are accessed as fields
  2665. of the object with the names of their respective base classes.
  2666. Classes derived in shared mode will create only a single base object.
  2667. Unambiguous grandchildren are inherited by the parent if they do not
  2668. collide with direct children.
  2669. ]]
  2670.  
  2671. local function build(class, shared_objs, shared,__root)
  2672.  
  2673. -- If shared, look in the repository of shared objects
  2674. -- and return any previous instance of this class.
  2675. if shared then
  2676. local prev_instance = shared_objs[class]
  2677. if prev_instance then return prev_instance end
  2678. end
  2679.  
  2680. -- Create new object
  2681. local obj = { __type = 'object' }
  2682. if __root==nil then
  2683. __root=obj
  2684. end
  2685. obj.__root=__root
  2686. obj[class.__name]=obj
  2687.  
  2688. -- Build child objects if there are base classes
  2689. local nbases = #class.__bases
  2690. if nbases > 0 then
  2691.  
  2692. -- Repository for storing inherited base objects
  2693. local inherited = {}
  2694.  
  2695. -- List of ambiguous keys
  2696. local ambiguous_keys = {}
  2697.  
  2698. -- Build child objects for each base class
  2699. for i = 1, nbases do
  2700. local base = class.__bases[i]
  2701. local child = build(base, shared_objs, class.__shared[base],__root)
  2702. obj[base.__name] = child
  2703.  
  2704. -- Get inherited grandchildren from this child
  2705. for c, grandchild in pairs(child) do
  2706.  
  2707. -- We can only accept one inherited grandchild of each class,
  2708. -- otherwise this is an ambiguous reference
  2709. if not ambiguous_keys[c] then
  2710. if not inherited[c] then inherited[c] = grandchild
  2711. elseif inherited[c] ~= grandchild then
  2712. inherited[c] = ambiguous
  2713. table.insert(ambiguous_keys, c)
  2714. end
  2715. end
  2716. end
  2717. end
  2718.  
  2719. -- Accept inherited grandchildren if they don't collide with
  2720. -- direct children
  2721. for k, v in pairs(inherited) do
  2722. if not obj[k] then obj[k] = v end
  2723. end
  2724.  
  2725. end
  2726.  
  2727. -- Object is ready
  2728. setmetatable(obj, class)
  2729.  
  2730. -- If shared, add it to the repository of shared objects
  2731. if shared then shared_objs[class] = obj end
  2732.  
  2733. return obj
  2734.  
  2735. end
  2736.  
  2737. --[[
  2738. The __call() operator creates an instance of the class and initializes it.
  2739. ]]
  2740.  
  2741. function class_mt:__call(...)
  2742. local obj = build(self, {}, false)
  2743. obj:__init(...)
  2744. return obj
  2745. end
  2746.  
  2747. --[[
  2748. The implements() method checks that an object or class supports the
  2749. interface of a target class. This means it can be passed as an argument to
  2750. any function that expects the target class. We consider only functions
  2751. and callable objects to be part of the interface of a class.
  2752. ]]
  2753.  
  2754. function class_mt:implements(class)
  2755.  
  2756. -- Auxiliary function to determine if something is callable
  2757. local function is_callable(v)
  2758. if v == ambiguous then return false end
  2759. if type(v) == 'function' then return true end
  2760. local mt = getmetatable(v)
  2761. return mt and type(mt.__call) == 'function'
  2762. end
  2763.  
  2764. -- Check we have all the target's callables (except reserved names)
  2765. for k, v in pairs(class) do
  2766. if not reserved[k] and is_callable(v) and not is_callable(self[k]) then
  2767. return false
  2768. end
  2769. end
  2770. return true
  2771. end
  2772.  
  2773. --[[
  2774. The is_a() method checks the type of an object or class starting from
  2775. its class and following the derivation chain upwards looking for
  2776. the target class. If the target class is found, it checks that its
  2777. interface is supported (this may fail in multiple inheritance because
  2778. of ambiguities).
  2779. ]]
  2780.  
  2781. function class_mt:is_a(class)
  2782.  
  2783. -- If our class is the target class this is trivially true
  2784. if self.__class == class then return true end
  2785.  
  2786. -- Auxiliary function to determine if a target class is one of a list of
  2787. -- classes or one of their bases
  2788. local function find(target, classlist)
  2789. for i = 1, #classlist do
  2790. local class = classlist[i]
  2791. if class == target or find(target, class.__bases) then
  2792. return true
  2793. end
  2794. end
  2795. return false
  2796. end
  2797.  
  2798. -- Check that we derive from the target
  2799. if not find(class, self.__bases) then return false end
  2800.  
  2801. -- Check that we implement the target's interface.
  2802. return self:implements(class)
  2803. end
  2804.  
  2805. --[[
  2806. Factory-supplied constructor, calls the user-supplied constructor if any,
  2807. then calls the constructors of the bases to initialize those that were
  2808. not initialized before. Objects are initialized exactly once.
  2809. ]]
  2810.  
  2811. function class_mt:__init(...)
  2812. if self.__initialized then return end
  2813. if self.__user_init then self:__user_init(...) end
  2814. for i = 1, #self.__bases do
  2815. local base = self.__bases[i]
  2816. self[base.__name]:__init(...)
  2817. end
  2818. self.__initialized = true
  2819. end
  2820.  
  2821.  
  2822. -- PUBLIC
  2823.  
  2824. --[[
  2825. Utility type and interface checking functions
  2826. ]]
  2827.  
  2828. function ofType(value)
  2829. local t = type(value)
  2830. return t =='table' and classname(t) or t
  2831. end
  2832.  
  2833. function typeof(value)
  2834. local t = type(value)
  2835. return t =='table' and value.__type or t
  2836. end
  2837.  
  2838. function classof(value)
  2839. local t = type(value)
  2840. return t == 'table' and value.__class or nil
  2841. end
  2842.  
  2843. function classname(value)
  2844. if not classof(value) then return nil end
  2845. local name = value.__name
  2846. return type(name) == 'string' and name or nil
  2847. end
  2848.  
  2849. function implements(value, class)
  2850. return classof(value) and value:implements(class) or false
  2851. end
  2852.  
  2853. function is_a(value, class)
  2854. return classof(value) and value:is_a(class) or false
  2855. end
  2856.  
  2857.  
  2858. --[[
  2859. Use a table to control class creation and naming.
  2860. ]]
  2861.  
  2862. class = {}
  2863. local mt = {}
  2864. setmetatable(class, mt)
  2865.  
  2866. --[[
  2867. Create a named or unnamed class by calling class([name, ] ...).
  2868. Arguments are an optional string to set the class name and the classes or
  2869. shared classes to be derived from.
  2870. ]]
  2871.  
  2872. function mt:__call(...)
  2873.  
  2874. local arg = {...}
  2875.  
  2876. -- Create a new class
  2877. local c =
  2878. {
  2879. __type = 'class',
  2880. __bases = {},
  2881. __shared = {}
  2882. }
  2883. c.__class = c
  2884. c.__index = c
  2885.  
  2886. -- A first string argument sets the name of the class.
  2887. if type(arg[1]) == 'string' then
  2888. c.__name = arg[1]
  2889. table.remove(arg, 1)
  2890. else
  2891. c.__name = c
  2892. end
  2893.  
  2894. -- Repository of inherited attributes
  2895. local inherited = {}
  2896. local from = {}
  2897.  
  2898. -- List of ambiguous keys
  2899. local ambiguous_keys = {}
  2900.  
  2901. -- Inherit from the base classes
  2902. for i = 1, #arg do
  2903. local base = arg[i]
  2904.  
  2905. -- Get the base and whether it is inherited in shared mode
  2906. local basetype = typeof(base)
  2907. local shared = basetype == 'share'
  2908. assert(basetype == 'class' or shared,
  2909. 'Base ' .. i .. ' is not a class or shared class')
  2910. if shared then base = base.__class end
  2911.  
  2912. -- Just in case, check this base is not repeated
  2913. assert(c.__shared[base] == nil, 'Base ' .. i .. ' is duplicated')
  2914.  
  2915. -- Accept it
  2916. c.__bases[i] = base
  2917. c.__shared[base] = shared
  2918.  
  2919. -- Get methods that could be inherited from this base
  2920. for k, v in pairs(base) do
  2921.  
  2922. -- Skip reserved and ambiguous methods
  2923. if type(v) == 'function' and not reserved[k] and (type(k)=='string' and not k:match'^_[^_]')and
  2924. not ambiguous_keys[k] then
  2925.  
  2926. -- Where does this method come from?
  2927. local new_from
  2928.  
  2929. -- Check if the method was inherited by the base
  2930. local base_inherited = base.__inherited[k]
  2931. if base_inherited then
  2932.  
  2933. -- If it has been redefined, cancel this inheritance
  2934. if base_inherited ~= v then -- (1)
  2935. base.__inherited[k] = nil
  2936. base.__from[k] = nil
  2937.  
  2938. -- It is still inherited, get it from the original
  2939. else
  2940. new_from = base.__from[k]
  2941. end
  2942. end
  2943.  
  2944. -- If it is not inherited by the base, it originates there
  2945. new_from = new_from or { class = base, shared = shared }
  2946.  
  2947. -- Accept a first-time inheritance
  2948. local current_from = from[k]
  2949. if not current_from then
  2950. from[k] = new_from
  2951. local origin = new_from.class
  2952.  
  2953. -- We assume this is an instance method (called with
  2954. -- self as first argument) and wrap it so that it will
  2955. -- receive the correct base object as self. For class
  2956. -- functions this code is unusable.
  2957. inherited[k] = function(self, ...)
  2958. return origin[k](self[origin.__name], ...)
  2959. end
  2960.  
  2961. -- Methods inherited more than once are ambiguous unless
  2962. -- they originate in the same shared class.
  2963. elseif current_from.class ~= new_from.class or
  2964. not current_from.shared or not new_from.shared then
  2965. inherited[k] = ambiguous
  2966. table.insert(ambiguous_keys, k)
  2967. from[k] = nil
  2968. end
  2969. end
  2970. end
  2971. end
  2972.  
  2973. -- Set the metatable now, it monitors attribute setting and does some
  2974. -- special processing for some of them.
  2975. setmetatable(c, class_mt)
  2976.  
  2977. -- Set inherited attributes in the class, they may be redefined afterwards
  2978. for k, v in pairs(inherited) do c[k] = v end -- checked at (1)
  2979. c.__inherited = inherited
  2980. c.__from = from
  2981.  
  2982. return c
  2983. end
  2984.  
  2985. --[[
  2986. Create a named class and assign it to a global variable of the same name.
  2987. Example: class.A(...) is equivalent to (global) A = class('A', ...).
  2988. ]]
  2989.  
  2990. function mt:__index(name)
  2991. return function(...)
  2992. local c = class(name, ...)
  2993. local cenv=getfenv(2)
  2994. cenv[name] = c
  2995. return c
  2996. end
  2997. end
  2998.  
  2999. --[[
  3000. Wrap a class for shared derivation.
  3001. ]]
  3002.  
  3003. function shared(class)
  3004. assert(typeof(class) == 'class', 'Argument is not a class')
  3005. return { __type = 'share', __class = class }
  3006. end
  3007. ]===================],"CoolisOS/bin/kernel/class/Opt.lua",[===================[class.Opt()
  3008. function Opt:__init(t,typ,...)
  3009. self.typ=typ or (typeof(t))
  3010. self.val=t
  3011. self.args={...}
  3012. end
  3013. function Opt:drop(dst,src,name)
  3014. local typ=self.typ
  3015. if typ=='req' then
  3016. if src==nil then error(string.format('parameter %s required',tostring(name)))
  3017. else return src end
  3018. elseif typ=='function' then if src==nil then return self.val(unpack(self.args)) else return src end
  3019. elseif typ=='raw' or typ=='nil' then
  3020. if src==nil then return self.val else return src end
  3021. elseif typ=='object' then return src==nil and (self.val.clone and self.val:clone() or self.val.__class(self.val)) or src
  3022. elseif typ=='class' then return src==nil and (self.val(unpack(self.args))) or src
  3023. elseif typ=='table' then
  3024. dst=dst or {}
  3025. src=src or {}
  3026. for i,v in pairs(self.val) do
  3027. if classof(v)==Opt then
  3028. dst[i]=v:drop(nil,src[i],i)
  3029. else
  3030. if src[i]~=nil then
  3031. v=src[i]
  3032. end
  3033. dst[i]=v
  3034. end
  3035. end
  3036. return dst
  3037. end
  3038. end
  3039. return Opt]===================],"CoolisOS/bin/kernel/class/Var.lua",[===================[include'kernel.class'
  3040. local check=require'kernel.checker'
  3041. local dump=require'utils.serpent'.dump
  3042. local util=require'kernel.util'
  3043. class.Var()
  3044. local p=setmetatable({},{__mode='k'})
  3045. function Var:__init(file,opt)
  3046. p[self]={}
  3047. p[self].file=file
  3048. local dir=loadreq.getDir(p[self].file)
  3049. if not fs.exists(dir) then fs.makeDir(dir) end
  3050. if not fs.exists(file) then self:setAll{} end
  3051. end
  3052.  
  3053. function Var:getAll()
  3054. local f=io.open(p[self].file,'r')
  3055. local s=f:read'*a'
  3056. f:close()
  3057. local t=loadstring(s)()
  3058. return t
  3059. end
  3060.  
  3061. function Var:setAll(t)
  3062. local f=io.open(p[self].file,'w')
  3063. local s=f:write(dump(t))
  3064. f:close()
  3065. end
  3066.  
  3067. function Var:__get(k)
  3068. return self:getAll()[k]
  3069. end
  3070. function Var:__set(k,v)
  3071. local t=self:getAll()
  3072. t[k]=v
  3073. self:setAll(t)
  3074. return true
  3075. end
  3076. return Var]===================],"CoolisOS/bin/kernel/gui",true,"CoolisOS/bin/kernel/gui/autocomplete.lua",[===================[
  3077. local list=require'utils.pl.List'
  3078. local pathutils={} --API
  3079. do
  3080. local segments, get, concat, find
  3081.  
  3082. local function pathconcat(pt, starti, endi)
  3083. local t = {}
  3084. local prev
  3085. local empties = 0
  3086. starti = starti or 1
  3087. endi = endi or #pt
  3088.  
  3089. for i = starti, endi do
  3090. local v = pt[i]
  3091. if not v then break
  3092. elseif v == '' then
  3093. empties = empties+1
  3094. else
  3095. table.insert(t, prev)
  3096. prev = v
  3097. end
  3098. end
  3099. table.insert(t, prev)
  3100. --log('PATH', 'INFO', "pathconcat(%s, %d, %d) generates table %s, wants indexes %d->%d",
  3101. -- sprint(pt), starti, endi, sprint(t), 1, endi-starti+1-empties)
  3102. return table.concat(t, '.', 1, endi-starti+1-empties)
  3103. end
  3104.  
  3105. function get(t, path)
  3106. local p = type(path)=='string' and segments(path) or path
  3107. local k = table.remove(p)
  3108. if not k then return t end
  3109. local t = find(t, p)
  3110. return t and t[k]
  3111. end
  3112.  
  3113.  
  3114.  
  3115. function segments(path)
  3116. local t = {}
  3117. local index, newindex, elt = 1
  3118. repeat
  3119. newindex = path:find(".", index, true) or #path+1 --last round
  3120. elt = path:sub(index, newindex-1)
  3121. elt = tonumber(elt) or elt
  3122. if elt and elt ~= "" then table.insert(t, elt) end
  3123. index = newindex+1
  3124. until newindex==#path+1
  3125. return t
  3126. end
  3127.  
  3128. function find(t, path, force)
  3129. path = type(path)=="string" and segments(path) or path
  3130. for i, n in ipairs(path) do
  3131. local v = t[n]
  3132. if type(v) ~= "table" then
  3133. if not force or (force=="noowr" and v~=nil) then return nil, pathconcat(path, 1, i)
  3134. else v = {} t[n] = v end
  3135. end
  3136. t = v
  3137. end
  3138. return t
  3139. end
  3140.  
  3141. pathutils.get=get
  3142. end
  3143.  
  3144. function env_matcher(path,opts)
  3145. local env=opts.env or _G
  3146.  
  3147. local lpath,pa = path:match("^(.-)([%w_]?[%w_%.%:]*)$") -- get the significant end part of the path (non alphanum are spliters...)
  3148.  
  3149. local p, s, l = pa:match("(.-)([%.%:]?)([^%.%:]*)$") -- separate into sub path and leaf, getting the separator
  3150. local t = pathutils.get(env, p)
  3151. local funconly = s == ":"
  3152. local tr = {}
  3153. local function copykeys(src, dst)
  3154. if type(src) ~= 'table' then return end
  3155. for k, v in pairs(src) do
  3156. local tv = type(v)
  3157. 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
  3158. end
  3159. end
  3160.  
  3161. copykeys(t, tr)
  3162.  
  3163. if s == ":" or s == "." then
  3164. local m = getmetatable(t)
  3165. if m then
  3166. local i, n = m.__index, m.__newindex
  3167. copykeys(i, tr)
  3168. if n ~= i then copykeys(n, tr) end
  3169. --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
  3170. end
  3171. end
  3172. local r = {}
  3173. for k, v in pairs(tr) do table.insert(r, k) end
  3174.  
  3175. -- sort the entries by lexical order
  3176. table.sort(r)
  3177. --when we have a complete match we can add a ".", ":" or "(" if the object is a table or a function
  3178. if l == r[1] then
  3179. local v=t[l]
  3180. if type(v)=='function' then
  3181. r[1]=r[1].."("
  3182. elseif type(v)=='table' then
  3183. if typeof(v)=='object' then
  3184. r[1]=r[1]..":"
  3185. else
  3186. r[1]=r[1].."."
  3187. end
  3188. end
  3189. end
  3190. for i,v in pairs(r) do r[i]=lpath..p..s..v end
  3191. return r
  3192. end
  3193.  
  3194. local tKeywords = {
  3195. ["and"] = true,
  3196. ["break"] = true,
  3197. ["do"] = true,
  3198. ["else"] = true,
  3199. ["elseif"] = true,
  3200. ["end"] = true,
  3201. ["false"] = true,
  3202. ["for"] = true,
  3203. ["function"] = true,
  3204. ["if"] = true,
  3205. ["in"] = true,
  3206. ["local"] = true,
  3207. ["nil"] = true,
  3208. ["not"] = true,
  3209. ["or"] = true,
  3210. ["repeat"] = true,
  3211. ["return"] = true,
  3212. ["then"] = true,
  3213. ["true"] = true,
  3214. ["until"]= true,
  3215. ["while"] = true,
  3216. }
  3217.  
  3218. function keyword_matcher(path,opts)
  3219. local r={}
  3220. if #path~=0 then
  3221. for key in pairs(tKeywords) do
  3222. if path==key:sub(1,#path) then
  3223. table.insert(r,key)
  3224. end
  3225. end
  3226. end
  3227. return r
  3228. end
  3229.  
  3230. local function esc(x)
  3231. return (x:gsub('%%', '%%%%')
  3232. :gsub('%^', '%%%^')
  3233. :gsub('%$', '%%%$')
  3234. :gsub('%(', '%%%(')
  3235. :gsub('%)', '%%%)')
  3236. :gsub('%.', '%%%.')
  3237. :gsub('%[', '%%%[')
  3238. :gsub('%]', '%%%]')
  3239. :gsub('%*', '%%%*')
  3240. :gsub('%+', '%%%+')
  3241. :gsub('%-', '%%%-')
  3242. :gsub('%?', '%%%?'))
  3243. end
  3244.  
  3245. function fs_matcher(word,opts)
  3246. local matches={}
  3247. local p,s,l
  3248. l=word:match("[^/\\]*$")
  3249. local cwd=opts.cwd or '/'
  3250. local absDir
  3251. if not word:match("[/\\][^/\\]*$") then
  3252. absDir=cwd
  3253. p,s='',''
  3254. else
  3255. p,s=word:match("^(.-)([/\\])[^/\\]*$")
  3256. absDir=(p=='' and '' or fs.combine(cwd,p))
  3257. end
  3258.  
  3259. if fs.isDir( absDir ) then
  3260. -- Search for matches in the resolved folder.
  3261. local ok, tFileList = pcall( fs.list, absDir )
  3262. if ok then
  3263. -- Populate table with all matches.
  3264. local pat="^"..esc(l)..'.*'
  3265. for k, v in ipairs(tFileList) do
  3266. local match = string.match( v, pat)
  3267. if match then
  3268. -- print(match)
  3269. table.insert( matches,match )
  3270. end
  3271. end
  3272. end
  3273. end
  3274. table.sort(matches)
  3275. if (l == matches[1]) and fs.isDir(fs.combine(absDir,l)) then
  3276. matches[1]= matches[1]..'/'
  3277. end
  3278. for i,v in pairs(matches) do matches[i]=p..s..v end
  3279. return matches
  3280. end
  3281.  
  3282. function getmatch(path,opts)
  3283. local r=list()
  3284. local matchers=opts.matchers
  3285. for _,matcher in pairs(matchers) do
  3286. r:extend(matcher(path,opts))
  3287. end
  3288. r:sort()
  3289. return r
  3290. end
  3291. ]===================],"CoolisOS/bin/kernel/gui/EvtH.lua",[===================[class.EvtH()
  3292. do
  3293. function EvtH:__init()
  3294.  
  3295. end
  3296.  
  3297. function EvtH:destroy()
  3298.  
  3299. end
  3300. function EvtH:queue()
  3301.  
  3302. end
  3303. wxEvtHandler::Connect
  3304. function EvtH:connect(src,event,f,...)
  3305. local cast=src.EvtH
  3306. local t=cast.dst[event] or setmetatable({},{__mode='k'})
  3307. cast.dst[event][self]=true
  3308.  
  3309. t=self.handlers[event] or {}
  3310. self.handlers[event]=t
  3311. t[f]={...}
  3312. end
  3313.  
  3314. wxEvtHandler::Disconnect
  3315. function EvtH:disconnect(src,event,f,this)
  3316. srcs=src and {src=true} or self.src
  3317. for src in pairs(srcs) do
  3318. events=event and {event=src[event]} or src.dst
  3319. for event,evth in pairs(events) do
  3320. fs=f and {f}
  3321. end
  3322. end
  3323. end
  3324. wxEvtHandler::GetClientData
  3325. function EvtH:()
  3326.  
  3327. end
  3328. wxEvtHandler::GetClientObject
  3329. function EvtH:()
  3330.  
  3331. end
  3332. wxEvtHandler::GetEvtHandlerEnabled
  3333. function EvtH:isEnabled()
  3334.  
  3335. end
  3336. wxEvtHandler::GetNextHandler
  3337. function EvtH:next()
  3338.  
  3339. end
  3340. wxEvtHandler::GetPreviousHandler
  3341. function EvtH:previous()
  3342.  
  3343. end
  3344. wxEvtHandler::ProcessEvent
  3345. function EvtH:process()
  3346.  
  3347. end
  3348. wxEvtHandler::SearchEventTable
  3349. function EvtH:search()
  3350.  
  3351. end
  3352. wxEvtHandler::SetClientData
  3353. function EvtH:()
  3354.  
  3355. end
  3356. wxEvtHandler::SetClientObject
  3357. function EvtH:()
  3358.  
  3359. end
  3360. function EvtH:setEnabled()
  3361.  
  3362. end
  3363. function EvtH:setNext()
  3364.  
  3365. end
  3366. function EvtH:setPrevious()
  3367.  
  3368. end
  3369. end]===================],"CoolisOS/bin/kernel/gui/init.lua",[===================[include'kernel.class'
  3370. local sched=require'kernel.sched'
  3371. local checker=require'kernel.checker'
  3372. local check=checker.check
  3373. local list=require'utils.pl.List'
  3374. gui=getfenv()
  3375. Point=require'Point'
  3376. Rectangle=require'Rectangle'
  3377. Term=require'Term'
  3378.  
  3379. function read(pin,term,opts)
  3380. pin=pin.Pipe
  3381. term:setCursorBlink(true)
  3382. local _sReplaceChar=opts._sReplaceChar
  3383. local _tHistory=opts.history
  3384. local sLine = ""
  3385. local nHistoryPos = nil
  3386. local nPos = 0
  3387. if _sReplaceChar then
  3388. _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
  3389. end
  3390.  
  3391. local auto
  3392. if opts.auto then
  3393. assert(#opts.auto>0)
  3394. auto={}
  3395. auto.mode=opts.auto.mode or 1
  3396. auto.count=0
  3397. auto.matches = { n = 0}
  3398. function auto.reset()
  3399. auto.matches = { n = 0}
  3400. end
  3401. local function split(s,n)
  3402. local s=sLine
  3403. local n=nPos+1
  3404. local l,r=s:sub(1,n-1),s:sub(n)
  3405. local pl,rl
  3406. if l:sub(-1,-1):match'%s' then
  3407. local sp=l:sub(-1,-1)
  3408. l=l:sub(1,-2)
  3409. r=sp..r
  3410. end
  3411.  
  3412. local ls,lp=l:match'^(.-)([^%s]+)$'
  3413. rp,rs=r:match'^([^%s]+)(.-)$'
  3414. if lp==nil then ls,lp=l,'','' end
  3415. if rp==nil then rp,rs='',r end
  3416. return ls,lp..rp,rs
  3417. end
  3418.  
  3419. function auto.update()
  3420. local l,p,r=split(sLine,nPos+1)
  3421. -- debug.log(l,p,r)
  3422. local ok,m=pcall(opts.auto[auto.mode],p)
  3423. if not ok then debug.log(m) end
  3424. -- debug.log(pstring(m,1))
  3425. m.l,m.r=l,r
  3426. auto.matches=m
  3427. end
  3428. function auto.put()
  3429. local matches=auto.matches
  3430. local match=matches[matches.n]
  3431. local l,r=matches.l,matches.r
  3432. sLine = l..match..r
  3433. nPos = #l+#match
  3434. end
  3435. end
  3436. local w, h = term:getSize()
  3437. local sx, sy = term:getCursorPos()
  3438.  
  3439.  
  3440. local function redraw( _sCustomReplaceChar )
  3441. local nScroll = 0
  3442. local su=''
  3443.  
  3444. if sx + nPos > w then
  3445. nScroll = (sx + nPos) - w
  3446. end
  3447. term:setCursorPos( sx, sy )
  3448.  
  3449. local sReplace = _sCustomReplaceChar or _sReplaceChar
  3450. local s
  3451. if sReplace then
  3452. s=string.rep(sReplace, string.len(sLine) - nScroll)
  3453. else
  3454. s=string.sub( sLine, nScroll + 1 )
  3455. end
  3456.  
  3457. if #s<(w-sx+1) then s=s..(' '):rep(w-sx+1) end
  3458. term:write(s)
  3459. term:setCursorPos( sx + nPos - nScroll, sy )
  3460. end
  3461.  
  3462.  
  3463. pin:reset()
  3464. while true do
  3465. local _,sEvent,param=unpack(pin:receive())
  3466. if sEvent == "char" then
  3467. if auto then auto.reset() end
  3468. sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
  3469. nPos = nPos + 1
  3470. redraw()
  3471. elseif sEvent == "key" then
  3472. if auto then
  3473. if not (param == keys.tab) then
  3474. auto.reset() -- Reset completion match-table.
  3475. else
  3476. if auto.matches[1] then -- tab was pressed before.
  3477. local matches=auto.matches
  3478. -- Shift pointer to next match.
  3479. matches.n = (matches.n)%#matches +1 -- Wrap around if the pointer has gone past the end.
  3480.  
  3481. -- Assemble the new line.
  3482. auto.put()
  3483.  
  3484. redraw()
  3485. else
  3486. -- [[ No matches yet, look for some now. ]]
  3487. auto.update()
  3488. local matches=auto.matches
  3489. matches.n=0
  3490. if #matches==0 then
  3491. --nPos=nPos+4
  3492. --sLine=sLine..'\t'
  3493. else -- Show first match.
  3494. matches.n=1
  3495. auto.put()
  3496. if #matches==1 then auto.reset() end
  3497. end
  3498. redraw()
  3499. end
  3500. end
  3501. if not (param == keys.rightShift) then
  3502. auto.count=0
  3503. else
  3504. auto.count=(auto.count+1)%2
  3505. if auto.count==0 then
  3506. auto.mode=(auto.mode)%#opts.auto+1
  3507. end
  3508. auto.reset()
  3509. end
  3510. end
  3511. if param == keys.enter then
  3512. -- Enter
  3513. break
  3514. elseif param == keys.left then
  3515. -- Left
  3516. if nPos > 0 then
  3517. nPos = nPos - 1
  3518. redraw()
  3519. end
  3520.  
  3521. elseif param == keys.right then
  3522. -- Right
  3523. if nPos < string.len(sLine) then
  3524. nPos = nPos + 1
  3525. redraw()
  3526. end
  3527.  
  3528. elseif param == keys.up or param == keys.down then
  3529. -- Up or down
  3530. if _tHistory then
  3531. redraw(" ");
  3532. if param == keys.up then
  3533. -- Up
  3534. if nHistoryPos == nil then
  3535. if #_tHistory > 0 then
  3536. nHistoryPos = #_tHistory
  3537. end
  3538. elseif nHistoryPos > 1 then
  3539. nHistoryPos = nHistoryPos - 1
  3540. end
  3541. else
  3542. -- Down
  3543. if nHistoryPos == #_tHistory then
  3544. nHistoryPos = nil
  3545. elseif nHistoryPos ~= nil then
  3546. nHistoryPos = nHistoryPos + 1
  3547. end
  3548. end
  3549.  
  3550. if nHistoryPos then
  3551. sLine = _tHistory[nHistoryPos]
  3552. nPos = string.len( sLine )
  3553. else
  3554. sLine = ""
  3555. nPos = 0
  3556. end
  3557. redraw()
  3558. end
  3559. elseif param == keys.backspace then
  3560. -- Backspace
  3561. if nPos > 0 then
  3562. redraw(" ");
  3563. sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
  3564. nPos = nPos - 1
  3565. redraw()
  3566. end
  3567. elseif param == keys.home then
  3568. -- Home
  3569. nPos = 0
  3570. redraw()
  3571. elseif param == keys.delete then
  3572. if nPos < string.len(sLine) then
  3573. redraw(" ");
  3574. sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
  3575. redraw()
  3576. end
  3577. elseif param == keys["end"] then
  3578. -- End
  3579. nPos = string.len(sLine)
  3580. redraw()
  3581. -- elseif param == keys.leftAlt then
  3582. -- write('\n'..'..')
  3583. -- if auto then opts.auto.mode=auto.mode end
  3584. -- return sLine..'\n'..read(pin,term,opts)
  3585. end
  3586. end
  3587. end
  3588.  
  3589. term:setCursorBlink( false )
  3590. term:setCursorPos( w + 1, sy )
  3591. if opts.clear~=false then print() end
  3592. return sLine
  3593. end
  3594. function write(term,sText)
  3595. check('string',sText)
  3596. local w,h = term:getSize()
  3597. local x,y = term:getCursorPos()
  3598.  
  3599. local nLinesPrinted = 0
  3600. local function newLine()
  3601. if y + 1 <= h then
  3602. term:setCursorPos(1, y + 1)
  3603. else
  3604. term:setCursorPos(1, h)
  3605. term:scroll(1)
  3606. end
  3607. x, y = term:getCursorPos()
  3608. nLinesPrinted = nLinesPrinted + 1
  3609. end
  3610.  
  3611. -- Print the line with proper word wrapping
  3612. while string.len(sText) > 0 do
  3613. local whitespace = string.match( sText, "^[ \t]+" )
  3614. if whitespace then
  3615. -- Print whitespace
  3616. term:write( whitespace )
  3617. x,y = term:getCursorPos()
  3618. sText = string.sub( sText, string.len(whitespace) + 1 )
  3619. end
  3620.  
  3621. local newline = string.match( sText, "^\n" )
  3622. if newline then
  3623. -- Print newlines
  3624. newLine()
  3625. sText = string.sub( sText, 2 )
  3626. end
  3627.  
  3628. local text = string.match( sText, "^[^ \t\n]+" )
  3629. if text then
  3630. sText = string.sub( sText, string.len(text) + 1 )
  3631. if string.len(text) > w then
  3632. -- Print a multiline word
  3633. while string.len( text ) > 0 do
  3634. if x > w then
  3635. newLine()
  3636. end
  3637. term:write( text )
  3638. text = string.sub( text, (w-x) + 2 )
  3639. x,y = term:getCursorPos()
  3640. end
  3641. else
  3642. -- Print a word normally
  3643. if x + string.len(text) - 1 > w then
  3644. newLine()
  3645. end
  3646. term:write( text )
  3647. x,y = term:getCursorPos()
  3648. end
  3649. end
  3650. end
  3651.  
  3652. return nLinesPrinted
  3653. end
  3654. function printError(term,s)
  3655. if term:isColour() then
  3656. t.term:setTextColour( colours.red )
  3657. end
  3658. gui.write(term,s)
  3659. term:setTextColour( colours.white )
  3660. end
  3661.  
  3662. local function property(class, attribute,field,setter,getter)
  3663. class['set'..attribute]=setter or function(self,val) self[field]=val end
  3664. class['get'..attribute]=getter or function(self) return self[field] end
  3665. end
  3666.  
  3667. local cc_events={
  3668. mouse={
  3669. 'mouse_click',
  3670. 'mouse_scroll',
  3671. 'mouse_drag',
  3672. },
  3673. touch={
  3674. 'monitor_touch',
  3675. },
  3676. keyboard={
  3677. 'key',
  3678. 'char',
  3679. },
  3680. }
  3681.  
  3682. class.Terminal()
  3683. do
  3684. function Terminal:__init(term,pin,pout)
  3685. check('Term,Pipe,Pipe',term,pin,pout)
  3686. self.term=term
  3687. debug.namefield(self,'term')
  3688. self.pin=pin
  3689. debug.namefield(self,'pin')
  3690. self.pout=pout
  3691. debug.namefield(self,'pout')
  3692. sched.task(sched.pump):run(pout,function(r) self:write(tostring(r)) end)
  3693. end
  3694.  
  3695. function Terminal.from_native(term,emitter)
  3696. check('native_terminal,!nil',term,emitter)
  3697. local h=sched.sigpipe()
  3698. if term.isColor() then
  3699. h:link{[emitter]=cc_events.mouse}
  3700. end
  3701. if term.setTextScale then
  3702. h:link{[emitter]=cc_events.touch}
  3703. else
  3704. h:link{[emitter]=cc_events.keyboard}
  3705. end
  3706. local t=Terminal(Term.from_native(term),h.Pipe,sched.pipe())
  3707. t.renderer=sched.task(function()
  3708. while true do
  3709. t.term:blit_native(term)
  3710. t.term:copyState(term)
  3711. sched.wait(false)
  3712. end
  3713. end)
  3714. t.renderer:link{[t.term]={'*'}}
  3715. debug.namefield(t,'renderer')
  3716. t.renderer:run()
  3717. t.term:setSignals(true)
  3718. t.term:clear()
  3719. return t
  3720. end
  3721.  
  3722. function Terminal:write(...)
  3723. return write(self.term,...)
  3724. end
  3725.  
  3726. function Terminal:read(...)
  3727. return read(self.pin,self.term,...)
  3728. end
  3729.  
  3730. end
  3731. ]===================],"CoolisOS/bin/kernel/gui/Point.lua",[===================[include'kernel.class'
  3732. local check=require'kernel.checker'.check
  3733. --[[-------------------------------------------------------
  3734. Members
  3735. Point.x -- number
  3736. Point.y -- number
  3737. Methods
  3738. Point:Point(x, y)
  3739. Point:Point(point)
  3740. Point:assign(x, y)
  3741. Point:assign(point)
  3742. Point:clone() -- return Point
  3743. Point:add(x, y)
  3744. Point:add(point)
  3745. Point:sub(x, y)
  3746. Point:sub(point)
  3747. Point:equal(point)
  3748. --------------------------------------------------------------------------]]
  3749. class.Point()
  3750.  
  3751. function Point:__init(x, y)
  3752. return self:assign(x, y)
  3753. end
  3754.  
  3755. function Point:assign(x, y)
  3756. if (not y) then
  3757.  
  3758. check('Point',x)
  3759.  
  3760. self.x = x.x
  3761. self.y = x.y
  3762. else
  3763. check('number,number',x,y)
  3764. self.x = x
  3765. self.y = y
  3766. end
  3767. return self
  3768. end
  3769.  
  3770. function Point:clone()
  3771. return Point(self)
  3772. end
  3773.  
  3774. function Point:add(x, y)
  3775. if (not y) then
  3776. check('Point',x)
  3777. self.x = self.x + x.x
  3778. self.y = self.y + x.y
  3779. else
  3780. check('number,number',x,y)
  3781. self.x = self.x + x
  3782. self.y = self.y + y
  3783. end
  3784. return self
  3785. end
  3786.  
  3787. function Point:sub(x, y)
  3788. if (not y) then
  3789. check('Point',x)
  3790. return self:add(-x.x, -x.y)
  3791. else
  3792. check('number,number',x,y)
  3793. return self:add(-x, -y)
  3794. end
  3795. end
  3796.  
  3797. function Point:equal(p)
  3798. check('Point',p)
  3799. return self.x == p.x and self.y == p.y
  3800. end
  3801. function Point:__tostring()
  3802. return '(' .. self.x .. ',' .. self.y ..')'
  3803. end
  3804.  
  3805. return Point
  3806. ]===================],"CoolisOS/bin/kernel/gui/Rectangle.lua",[===================[include'kernel.class'
  3807. local Point=require'kernel.gui.Point'
  3808. class.Rectangle()
  3809. --[[ -------------------------------------------------------
  3810. Members:
  3811. Rectangle.s -- Point (start)
  3812. Rectangle.e -- Point (end)
  3813. Methods:
  3814. Rectangle:assign(x1, y1, x2, y2)
  3815. Rectangle:assign(rect)
  3816. Rectangle:size() -- return Point
  3817. Rectangle:clone() -- return Rectangle
  3818. Rectangle:move(deltax, deltay)
  3819. Rectangle:grow(deltax, deltay)
  3820. Rectangle:intersect(rect)
  3821. Rectangle:union(rect)
  3822. Rectangle:equal(rect)
  3823. Rectangle:contains(point)
  3824. Rectangle:empty()
  3825. Rectangle:nempty()
  3826. --------------------------------------------------------------------------]]
  3827. function Rectangle:__init(x1, y1, x2, y2)
  3828. self:assign(x1, y1, x2, y2)
  3829. end
  3830.  
  3831. function Rectangle:assign(x1, y1, x2, y2)
  3832. if (not y1) then
  3833. self.s = x1.s:clone()
  3834. self.e = x1.e:clone()
  3835. elseif (not x2) then
  3836. self.s=x1:clone()
  3837. self.e=y1:clone()
  3838. else
  3839. self.s = Point(x1, y1)
  3840. self.e = Point(x2, y2)
  3841. end
  3842. return self
  3843. end
  3844.  
  3845. function Rectangle:size()
  3846. return self.e:clone():sub(self.s)
  3847. end
  3848.  
  3849. function Rectangle:clone()
  3850. return Rectangle(self)
  3851. end
  3852.  
  3853. function Rectangle:move(deltax, deltay)
  3854. self.s:add(deltax, deltay)
  3855. self.e:add(deltax, deltay)
  3856. return self
  3857. end
  3858.  
  3859. function Rectangle:grow(deltax, deltay)
  3860. self.s:sub(deltax, deltay)
  3861. self.e:add(deltax, deltay)
  3862. return self
  3863. end
  3864.  
  3865. function Rectangle:intersect(r)
  3866. local s, e = self.s, self.e
  3867.  
  3868. s.x = max(s.x, r.s.x)
  3869. s.y = max(s.y, r.s.y)
  3870. e.x = min(e.x, r.e.x)
  3871. e.y = min(e.y, r.e.y)
  3872. return self
  3873. end
  3874.  
  3875. function Rectangle:union(r)
  3876. local s, e = self.s, self.e
  3877.  
  3878. s.x = min(s.x, r.s.x)
  3879. s.y = min(s.y, r.s.y)
  3880. e.x = max(e.x, r.e.x)
  3881. e.y = max(e.y, r.e.y)
  3882. return self
  3883. end
  3884.  
  3885. function Rectangle:equal(r)
  3886. return self.s:equal(r.s) and self.e:equal(r.e)
  3887. end
  3888.  
  3889. function Rectangle:contains(x, y)
  3890. return x >= self.s.x and x < self.e.x and y >= self.s.y and y < self.e.y
  3891. end
  3892.  
  3893. function Rectangle:empty()
  3894. return self.s.x >= self.e.x or self.s.y >= self.e.y
  3895. end
  3896.  
  3897. function Rectangle:nempty()
  3898. return not self:empty()
  3899. end
  3900.  
  3901.  
  3902. function Rectangle:__tostring()
  3903. if (self:empty()) then
  3904. return 'rect (empty)'
  3905. else
  3906. return 'rect (' .. tostring(self.s) .. ',' .. tostring(self.e) .. ')'
  3907. end
  3908. end
  3909. return Rectangle]===================],"CoolisOS/bin/kernel/gui/shell.lua",[===================[include'kernel.gui'
  3910. local check=require'kernel.checker'.check
  3911. local acpl=require'kernel.gui.autocomplete'
  3912. local Box=require'kernel.class.Box'
  3913. --[[
  3914. methods:
  3915. loop
  3916. execute string->command->stuff
  3917.  
  3918.  
  3919. runFile
  3920. runLua
  3921.  
  3922. destroy
  3923. fields:
  3924. cwd
  3925. tasks
  3926. env
  3927.  
  3928. input history
  3929. output history
  3930.  
  3931. pin events
  3932. pcmd commands
  3933. defaults:
  3934. cwd
  3935. path
  3936.  
  3937.  
  3938. Full craftos programs support:
  3939.  
  3940. special option:
  3941.  
  3942. ]]
  3943.  
  3944. function new()
  3945. local shell={}
  3946. shell.env=setmetatable({},{__index=_BOX.env})
  3947. shell.env.shell=shell
  3948. local prompts={
  3949. normal='>',
  3950. }
  3951.  
  3952. --[[
  3953. input
  3954. pout output
  3955. ]]
  3956.  
  3957. --filesystem
  3958. shell.cwd='/'
  3959. shell.path=fs.combine(_INSTALL_PATH,'bin/programs')
  3960. function shell.resolve(sPath)
  3961. check('string',sPath)
  3962. local sStartChar = string.sub( sPath, 1, 1 )
  3963. if sStartChar == "/" or sStartChar == "\\" then
  3964. return fs.combine( "", sPath )
  3965. else
  3966. return fs.combine( shell.cwd, sPath )
  3967. end
  3968. end
  3969. ----intertask
  3970. shell.pin=sched.pipe()
  3971. --rendering
  3972. local w,h=term:getSize()
  3973. local up_term=Term{size=Point(w,1)}
  3974. local down_term=Term{size=Point(w,h-1)}
  3975. up_term:setSignals(true)
  3976. down_term:setSignals(true)
  3977. -- debug.name(up_term,'up_term')
  3978. -- debug.name(down_term,'down_term')
  3979. sched.task(function()
  3980. sched.running:link{[up_term]={'*'},[down_term]={'*'}}
  3981. repeat
  3982. up_term:blit(term)
  3983. down_term:blit(term,Point(1,2))
  3984. sched.wait(false)
  3985. until false
  3986. end):run()
  3987. --piping stuff to proper terms
  3988. local up_pin=_BOX.terminal.pin
  3989. local function _read()
  3990. up_term:clear()
  3991. up_term:setCursorPos(1,1)
  3992. gui.write(up_term,shell.mode..shell.prompt)
  3993. return gui.read(up_pin,up_term,{clear=false,history=shell.input,auto={
  3994. function(s) return acpl.getmatch(s,{env=shell.env,
  3995. matchers={
  3996. acpl.keyword_matcher,
  3997. acpl.env_matcher,
  3998. },
  3999. }
  4000. )
  4001. end,
  4002. function(s) return acpl.fs_matcher(s,{cwd=shell.cwd}) end,
  4003. mode=shell.mode=='=' and 1 or 2,
  4004. }
  4005. }
  4006. )
  4007. end
  4008. sched.task(function() sched.pump(nil,function() sched.wait() return _read() end,shell.pin) end):run()
  4009.  
  4010. sched.task(sched.pump):run(shell.pin,function(s)
  4011. shell.execute(s)
  4012. end)
  4013. shell.pout=sched.pipe()
  4014. sched.task(sched.pump):run(shell.pout,function(s)
  4015. gui.write(down_term,s..'\n')
  4016. end)
  4017.  
  4018. shell.perr=sched.pipe()
  4019. sched.task(sched.pump):run(shell.perr,function(s)
  4020. log('shell','error',s)
  4021. gui.printError(down_term,'perr:'..s..'\n')
  4022. end)
  4023.  
  4024. --keeping track of running tasks
  4025. shell.tasks=sched.Catalog()
  4026.  
  4027. shell.trace=false
  4028. function shell.runFunc(f,trace,stat,path)
  4029. -- local env=setmetatable({},{__index=shell.env})
  4030. local box=Box{pout=shell.pout,perr=shell.perr,env=shell.env}
  4031. local func=function() return box:run(f) end
  4032.  
  4033. local t=sched.task(function()
  4034. if trace==nil then trace=shell.trace end
  4035. local t =trace and { xpcall(func,debug.traceback) } or { pcall(func) }
  4036. if t[1] then
  4037. table.remove(t,1)
  4038. table.insert(shell.output,t)
  4039. shell.env._=t[1]
  4040. local n=table.maxn(t)
  4041. if n~=0 or stat then
  4042. s=tostring(t[1])
  4043. for i=2,n do
  4044. s=s..','..tostring(t[i])
  4045. end
  4046. shell.pout:send(s)
  4047. end
  4048. else
  4049. shell.perr:send(t[2] or 'error(no msg)')
  4050. end
  4051. end)
  4052. debug.name(t,'in'..#shell.input)
  4053. shell.tasks:register(t,'in'..#shell.input)
  4054. t:run()
  4055. end
  4056. function shell.runFile(path)
  4057. local f,e=io.open(path,'r')
  4058. if not f then shell.perr:send(e or 'could not open file')
  4059. else
  4060. local st=f:read'*a'
  4061. f:close()
  4062. local f,e=loadstring(st,path)
  4063. if not f then shell.perr:send(e)
  4064. else
  4065. shell.runFunc(f,true,nil,path)
  4066. end
  4067. end
  4068. end
  4069.  
  4070. shell.input={}
  4071. shell.output=setmetatable({},{__index=function(t,k) return unpack(rawget(t,k)) end})
  4072. shell.env._in=shell.input
  4073. shell.env._out=shell.output
  4074. shell.prompt=prompts.normal
  4075. shell.mode='='
  4076. local modes={
  4077. ['>']=function(s,name) --filesystem mode
  4078. s=shell.resolve(s)
  4079. shell.runFile(s)
  4080. end,
  4081. ['=']=function(s,name)
  4082. local proper_loadstring=function(s,n)
  4083. check('string,string',s,n)
  4084. local ok,f,e=pcall(loadstring,s,n)
  4085. if e then e=e:match'(%d:[^:]-)$' end
  4086. return f,e
  4087. end
  4088.  
  4089. local function get(s,name)
  4090. --xpcall catches parsing errors which normally are returned as nil, error_msg.
  4091. --so we pcall loadstring to avoid loosing the error
  4092. --strangely, the xpcall lets execution continue at this level
  4093. local func,e
  4094. local f1,e1=proper_loadstring(s,name)
  4095. local f2, e2 = proper_loadstring( "return "..s,name)
  4096. if f1 then
  4097. if f2 then
  4098. func=f2
  4099. else
  4100. func=f1
  4101. end
  4102. else
  4103. if f2 then
  4104. func=f2
  4105. else
  4106. e=e1
  4107. end
  4108. end
  4109. if e then e=name..':'..e end
  4110. return func,e,(func==f2)
  4111. end
  4112. local func,e,stat=get(s,name)
  4113.  
  4114. if func then
  4115. shell.runFunc(func,nil,stat)
  4116. else
  4117. shell.perr:send(e)
  4118. end
  4119.  
  4120. end
  4121. }
  4122. --history
  4123.  
  4124. function shell.execute(s)
  4125. table.insert(shell.input,s)
  4126. local name='in['..#shell.input..']'
  4127. shell.pout:send(name..':'..s)
  4128. if modes[s:sub(1,1)] then--mode change
  4129. shell.mode=s:sub(1,1)
  4130. s=s:sub(2)
  4131. end
  4132. if #s>0 then modes[shell.mode](s,name) end
  4133. end
  4134.  
  4135. function shell.run_startups()
  4136. local dir=fs.combine(_INSTALL_PATH,'ect/startups')
  4137. if not fs.exists(dir) then fs.makeDir(dir) end
  4138. local st=fs.list(dir)
  4139. table.sort(st)
  4140. for i,l in pairs(st) do
  4141. shell.runFile(fs.combine(dir,l))
  4142. end
  4143. end
  4144.  
  4145.  
  4146. _BOX.env.shell=shell
  4147. return shell
  4148. end
  4149. ]===================],"CoolisOS/bin/kernel/gui/Term.lua",[===================[include'kernel.class'
  4150. local Opt=require'kernel.class.Opt'
  4151. local checker=require'kernel.checker'
  4152. local check=checker.check
  4153. local Point=require'Point'
  4154. local Rectangle=require'Rectangle'
  4155. -- local function nimpl() return error('Not implemented',3) end
  4156. -- class.DC()
  4157. -- function DC:blit(pos,section) nimpl() end
  4158. -- function DC:setCursorPos(pos,section) nimpl() end
  4159. -- function DC:write(txt) nimpl() end
  4160. -- function DC:setTextColor(pos,section) nimpl() end
  4161. -- function DC:setBackgroundColor(pos,section) nimpl() end
  4162.  
  4163. class.Term()
  4164. local term_opt=Opt{
  4165. size=Opt(Point(5,5)),
  4166. bcolor=colors.black,
  4167. tcolor=colors.white,
  4168. pos=Opt(Point(1,1)),
  4169. color=term.isColor(),
  4170. blink=false,
  4171. }
  4172. local function update(self) self:emit(UPDATE) end
  4173. checker.custom.color=function(n) return (type(n)=='number' and n>=0 or error('Invalid color, must be number >0',3)) end
  4174. checker.custom.char=function(s) return type(s)=='string' and #s==1 end
  4175. local native=util.shcopy(term.native)
  4176. checker.custom.native_terminal=function(t)
  4177. for i,v in pairs(native) do
  4178. if type(t[i])~=type(native[i]) then return false end
  4179. end
  4180. if typeof(t)~='table' then return false end
  4181. return true
  4182. end
  4183. function Term.from_native(term)
  4184. check('native_terminal',term)
  4185. return Term{
  4186. size=Point(term.getSize()),
  4187. color=term.isColor(),
  4188. blink=true,
  4189. }
  4190. end
  4191. local function wrap_obj(self,tMethods)
  4192. for i,v in pairs(tMethods or self) do
  4193. if type(v)=='function' then
  4194. local f=self[i]
  4195. self[i]=function(s,...)
  4196. if self==s then
  4197. return f(s,...)
  4198. else
  4199. return f(self,s,...)
  4200. end
  4201. end
  4202. end
  4203. end
  4204. end
  4205. --[[process
  4206. e.g. shell
  4207. in out err
  4208. term
  4209. ---in out in out
  4210. ]]
  4211. local active={
  4212. 'setBackgoundColour',
  4213. 'setTextColour',
  4214. 'setCursorPos',
  4215. 'setCursorBlink',
  4216. 'write',
  4217. 'setSize',
  4218. 'scroll',
  4219. 'clear',
  4220. 'setPixel',
  4221. }
  4222. function Term:setSignals(bool)
  4223. for _,k in ipairs(active) do
  4224. local f=self['_'..k] or self[k]
  4225. self[k]=bool and function(...)
  4226. sched.signal(self,k,...)
  4227. return f(...)
  4228. end or f
  4229. end
  4230. end
  4231. function Term:__init(opt)
  4232. term_opt:drop(self,opt)
  4233. self:_allocate()
  4234. wrap_obj(self,native)
  4235. end
  4236. function Term:setCursorPos(x,y) self.pos:assign(x,y) end
  4237. function Term:getCursorPos() return self.pos.x,self.pos.y end
  4238. function Term:setBackgroundColor(color) check('color',color) self.bcolor=color end
  4239. Term.setBackgroundColour=Term.setBackgroundColor
  4240. function Term:getBackgroundColor() return self.bcolor end
  4241. Term.getBackgroundColour=Term.getBackgroundColor
  4242. function Term:setTextColor(color) check('color',color) self.tcolor=color end
  4243. Term.setTextColour=Term.setTextColor
  4244. function Term:getTextColor() return self.tcolor end
  4245. Term.getTextColour=Term.getTextColor
  4246. function Term:isColor() return self.color end
  4247. Term.isColour=Term.isColor
  4248. function Term:setCursorBlink(bool) check('boolean',bool) self.blink=bool end
  4249. function Term:getCursorBlink() return self.blink end
  4250. function Term:write(txt)
  4251. check('string',txt)
  4252. local n=#txt
  4253. local pos=self.pos
  4254. if pos.x+n<=1 or pos.x> self.size.x then
  4255. pos.x=pos.x+n
  4256. return
  4257. end
  4258. if pos.x < 1 then
  4259. txt=txt:sub(math.abs(pos.x)+2)
  4260. pos.x=1
  4261. end
  4262. local line=self.matrix[pos.y]
  4263. if line then
  4264. for j=pos.x,math.min(pos.x-1+n,self.size.x) do
  4265. line[j]={tc=self.tcolor,bc=self.bcolor,c=txt:sub(j-pos.x+1,j-pos.x+1)}
  4266. end
  4267. end
  4268. pos.x=pos.x+n
  4269. end
  4270. function Term:setSize(self,size)
  4271. self.size=size
  4272. self:_allocate()
  4273. end
  4274. function Term:getSize()
  4275. return self.size.x,self.size.y
  4276. end
  4277.  
  4278. function Term:scroll(n)
  4279. check('number',n)
  4280. n=(n-n%1)
  4281. local ind,nind=n>0 and 1 or nil,n>0 and self.size.y or 1
  4282. local matrix=self.matrix
  4283. for i=1,math.abs(n) do
  4284. table.remove(matrix,ind)
  4285. matrix[nind]=self:_newLine()
  4286. end
  4287. end
  4288. function Term:clear() self:_allocate() end
  4289. function Term:clearLine()
  4290. if self.matrix[self.pos.y] then
  4291. self.matrix[self.pos.y]=self:_newLine()
  4292. end
  4293. end
  4294. function Term:_newLine()
  4295. local line={}
  4296. for j=1,self.size.x do
  4297. line[j]={tc=self.tcolor,bc=self.bcolor,c=' '}
  4298. end
  4299. return line
  4300. end
  4301. function Term:_allocate()
  4302. local X,Y = self.size.x,self.size.y
  4303. self.matrix=self.matrix or {}
  4304. for i=1,Y do
  4305. self.matrix[i]=self:_newLine()
  4306. end
  4307. end
  4308. local def_pos=Point(1,1)
  4309. function Term:blit_native(term,pos,section)
  4310. pos=pos or def_pos
  4311. section=section or Rectangle(def_pos,self.size)
  4312. term.setCursorBlink(false)
  4313. if self.blink then
  4314. local cell=self.matrix[self.pos.y] and self.matrix[self.pos.y][self.pos.x]
  4315. if cell then
  4316. self._bcell=cell
  4317. cell.bc,cell.tc=cell.tc,cell.bc
  4318. else
  4319. self._bcell=nil
  4320. end
  4321. end
  4322. for i=section.s.y,section.e.y do
  4323. local line=self.matrix[i]
  4324. local tc,bc=line[1].tc,line[1].bc
  4325. local tTxt={}
  4326. for j=section.s.x,section.e.x do
  4327. local tl,bl=line[j].tc,line[j].bc
  4328. if tc~=tl or bc~=bl then
  4329. term.setCursorPos(j-#tTxt+pos.x-1,i+pos.y-1)
  4330. term.setTextColor(tc==0 and self.tcolor or tc)
  4331. term.setBackgroundColor(bc==0 and self.bcolor or bc)
  4332. term.write(table.concat(tTxt))
  4333. tc,bc=tl,bl
  4334. tTxt={}
  4335. end
  4336. table.insert(tTxt,line[j].c)
  4337. end
  4338. if tTxt[1] then
  4339. term.setCursorPos(section.e.x-#tTxt+pos.x,i+pos.y-1)
  4340. term.setTextColor(tc==0 and self.tcolor or tc)
  4341. term.setBackgroundColor(bc==0 and self.bcolor or bc)
  4342. term.write(table.concat(tTxt))
  4343. end
  4344. end
  4345. if self._bcell then
  4346. local cell=self._bcell
  4347. cell.bc,cell.tc=cell.tc,cell.bc
  4348. self._bcell=nil
  4349. end
  4350. end
  4351. function Term:blit(term,pos,section)
  4352. return self:blit_native(term,pos,section) --temporary?
  4353. end
  4354.  
  4355. function Term:setPixel(char,tcolor,bcolor,x,y)
  4356. check('char,?|color,?|color,?|Point|number|table,?|number',char,tcolor,bcolor,x,y)
  4357.  
  4358. if type(x)=='table' then
  4359. x,y=x.x,x.y
  4360. check('number,number',x,y)
  4361. else
  4362. x,y=x or self.pos.x, y or self.pos.y
  4363. end
  4364. if self.matrix[y] and self.matrix[y][x] then
  4365. self.matrix[y][x]={c=char or ' ',tc=tcolor or self.tcolor,bc=bcolor or self.bcolor}
  4366. end
  4367. end
  4368. function Term:copyState(term)
  4369. term.setTextColor(self:getTextColor())
  4370. term.setBackgroundColor(self:getBackgroundColor())
  4371. term.setCursorPos(self:getCursorPos())
  4372. end
  4373.  
  4374. -- function Term:from_npaint(file)
  4375. -- -- pos=pos or def_pos
  4376. -- -- section=section or Rectangle(pos,self.size)
  4377.  
  4378. -- for line in file:lines() do
  4379. -- if section.x<
  4380. -- end
  4381. -- end
  4382.  
  4383. function Term:_dump_key(k)
  4384. local s={}
  4385. for i=1, self.size.y do
  4386. local txt={}
  4387. 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)
  4388. txt=table.concat(txt)
  4389. table.insert(s,txt)
  4390. end
  4391. return table.concat(s,'\n')
  4392. end
  4393. function Term:dump()
  4394. return self:_dump_key'c',self:_dump_key'tc',self:_dump_key'bc'
  4395. end
  4396. function Term:blit_all(term)
  4397. for i=1,self.size.y do
  4398. local line=self.matrix[i]
  4399. for j=1,self.size.x do
  4400. local p=line[j]
  4401. term.setCursorPos(j,i)
  4402. term.setTextColor(p.tc)
  4403. term.setBackgroundColor(p.bc)
  4404. term.write(p.c)
  4405. end
  4406. end
  4407. end
  4408. -- assert(Term:implements(DC))
  4409. Term.test=function()
  4410. rawset(_G,'t',Term{size=Point(term.getSize())})
  4411. for i=1,t.size.y do
  4412. t:setPixel('a',2^i,2^i,i,i)
  4413. end
  4414. sleep(1)
  4415. term.clear()
  4416. -- t:setCursorPos(2,2)
  4417. t:blit_native(term)
  4418. sleep(1)
  4419. end
  4420.  
  4421.  
  4422. 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.
  4423. --Licensed under the MIT license, whatever that is.
  4424.  
  4425.  
  4426. local fs=fs
  4427. local string=string
  4428. local loadfile=loadfile
  4429. local error=error
  4430. local function log(...)
  4431. return (_G.log and _G.log(...))
  4432. end
  4433.  
  4434. function getNameExpansion(s) --returns name and expansion from a filepath @s; special cases: ''->'',nil; '.'-> '','';
  4435. local _,_,name,expa=string.find(s, '([^%./\\]*)%.(.*)$')
  4436. return name or s,expa
  4437. end
  4438.  
  4439. function getDir(s) --returns directory from filepath @s
  4440. return string.match(s,'^(.*)/') or '/'
  4441. end
  4442.  
  4443. vars={} --to allow edition
  4444. --vars.loaded={}
  4445. --function loadFile(path,_reload) --@_reload forces reload
  4446. -- if (vars.loaded[path]==nil) or _reload then
  4447. -- local fnFile, err = loadfile( path )
  4448. -- if not fnFile then error('load:'..'path=:'..path..'| '..(err or 'nil'),2) end
  4449. -- vars.loaded[path]=fnFile
  4450. -- end
  4451. -- return vars.loaded[path]
  4452. --end
  4453. --
  4454. ---iterator; replaces '?' in g by s and returns the resulting path if it is a file.
  4455. local function direct(g,s)
  4456. g=string.gsub(g,'%?',s)
  4457. if fs.exists(g) and not fs.isDir(g) then
  4458. return g
  4459. end
  4460. end
  4461. --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.
  4462. vars.finders={direct}
  4463. vars.paths='?/init.lua;?.lua'
  4464.  
  4465. --helper vars for lua_requirer; other requirers may index theirs vars in loadreq.vars
  4466. vars.lua_requirer={
  4467. required={}, --to unrequire filepath s do require[s]=nil
  4468. required_envs={}, --to prevent garbage collection
  4469. requiring={}, --to throw error in case of recursive requiring;
  4470. }
  4471.  
  4472. --[[lua_requirer(path,cenv,env,renv,rerun,args)
  4473. Accepts empty or .lua extensions.
  4474. if the rerun flag is true, reloads the file even if it done it before;
  4475. if the file has been loaded already returns previous value;
  4476. if the file is being loaded returns nil, error_message
  4477. else:
  4478. loads file in @path;
  4479. sets it's env to @env, default {} with metatable with __index set to @renv, default _G;
  4480. calls the function with unpack(@args) and returns and saves either
  4481. the function return
  4482. a shallow copy of the functions environment
  4483. ]]
  4484.  
  4485. function lua_requirer(path,cenv,t)
  4486. local env,renv,rerun,args=t.env,t.renv,t.rerun,t.args
  4487.  
  4488. local err_prefix='lua_requirer:'
  4489. local vars=vars.lua_requirer
  4490. local _,ext=getNameExpansion(path)
  4491. if not (ext=='' or ext=='lua' or ext==nil) then
  4492. return nil, err_prefix..'wrong extension:'..ext
  4493. end
  4494.  
  4495. if vars.requiring[path] then
  4496. return nil, err_prefix..'file is being loaded'
  4497. end
  4498. if not rerun and vars.required[path] then
  4499. return vars.required[path]
  4500. end
  4501.  
  4502. local f,e=loadfile(path,path)
  4503. if not f then
  4504. return nil,err_prefix..'loadfile:'..e
  4505. end
  4506. env=env or {}
  4507. env._FILE_PATH=path
  4508. vars.required_envs[path]=env
  4509. setfenv(f,env)
  4510. renv=renv or _G
  4511. setmetatable(env,{__index=renv})
  4512. vars.requiring[path]=true
  4513. local ok,r=pcall(f,args)
  4514. vars.requiring[path]=nil
  4515. if not ok then
  4516. return nil,err_prefix..'while calling module:\n'..r
  4517. elseif r then
  4518. vars.required[path]=r
  4519. return r
  4520. else
  4521. local t={}
  4522. for i,v in pairs(env) do
  4523. t[i]=v
  4524. end
  4525. vars.required[path]=t
  4526. return t
  4527. end
  4528. end
  4529.  
  4530. --replacement for os_loadAPI that keeps APIS to the caller's env
  4531. --local old_os_loadAPI=os.loadAPI
  4532. --new_os_loadAPI=function(p)
  4533. -- local cenv=getfenv(2)
  4534. -- local r,e=loadreq.lua_requirer(p,cenv)
  4535. -- if r then
  4536. -- local name=getNameExpansion(p)
  4537. -- protect(r)
  4538. -- cenv[p]=r
  4539. -- else
  4540. -- error('os.loadAPI(loadreq version):'..e..'\n'..'path='..p,2)
  4541. -- end
  4542. --end
  4543.  
  4544. --[[add your requirers here;
  4545. each must
  4546. take as arguments (path,cenv,...) where
  4547. path is the path to required file
  4548. cenv is the environment of the caller of @require
  4549. ... are extra arguments passed to @require
  4550. return
  4551. one value to be returned by @require
  4552. false|nil, error_message in case of failure
  4553. ]]
  4554. vars.requirers={lua=lua_requirer}
  4555.  
  4556. function sufix(s)
  4557. return string.gsub('@/?;@/?.lua;@/?/init.lua;@/?/?.lua;@/?/?;@','@',s)
  4558. end
  4559.  
  4560.  
  4561. local function _find(s,paths,caller_env)
  4562. local err={'_find: finding '..tostring(s)}
  4563.  
  4564. if paths then
  4565. elseif caller_env.REQUIRE_PATH then
  4566. paths=caller_env.REQUIRE_PATH
  4567. elseif caller_env.PACKAGE_NAME and caller_env._FILE_PATH then
  4568. paths=sufix(string.match(caller_env._FILE_PATH,'^(.-'..caller_env.PACKAGE_NAME..')'))..';'..vars.paths
  4569. elseif caller_env._FILE_PATH then
  4570. paths=sufix(getDir(caller_env._FILE_PATH))..';'..vars.paths
  4571. else
  4572. paths=vars.paths
  4573. end
  4574.  
  4575. --replace . by / and .. by .
  4576. s=string.gsub(s,'([^%.])%.([^%.])','%1/%2')
  4577. s=string.gsub(s,'^%.([^%.])','/%1')
  4578. s=string.gsub(s,'%.%.','.')
  4579.  
  4580. local finders=vars.finders
  4581.  
  4582. local finder,path
  4583. for i=1,#finders do
  4584. finder=finders[i]
  4585. for search_path in string.gmatch(paths,';?([^;]+);?') do
  4586. path=finder(search_path,s)
  4587. if path then return path end
  4588.  
  4589. end
  4590. end
  4591. table.insert(err,'_find:file not found:'..s..'\ncaller path='..(caller_env._FILE_PATH or 'not available'))
  4592. local serr=table.concat(err,'\n')
  4593. log('loadreq','ERROR','_find:%s',serr)
  4594. return nil,serr
  4595. end
  4596. find=_find
  4597.  
  4598. local function _require(s,opt,caller_env)
  4599. local err={}
  4600. table.insert(err,'loadreq:require: while requiring '..tostring(s))
  4601. local path,e=_find(s,opt.path,caller_env)
  4602. if path==nil then
  4603. table.insert(err,e)
  4604. return nil, table.concat(err,'\n')
  4605. end
  4606. for req_name,requirer in pairs(vars.requirers) do
  4607. local r,e=requirer(path,caller_env,opt)
  4608. if r then
  4609. return r,path
  4610. else
  4611. table.insert(err,e)
  4612. end
  4613. end
  4614. return nil, table.concat(err,'\n')
  4615. end
  4616.  
  4617. --[[require(s,paths,...)
  4618. @paths is a string of paths separated by ';' where there can be '?'
  4619. -acquires @paths variable, by the following order;
  4620. 0-arg @paths
  4621. Example (_FILE_PATH='myFolder/myFolder2/myAPI.lua'):
  4622. myAPI=require('myFolder2.myAPI','myFolder/?.lua')
  4623. 1-REQUIRE_PATH in the caller's path, if existent
  4624. Example (_FILE_PATH='myFolder/myFolder2/myAPI.lua'):
  4625. REQUIRE_PATH='myFolder/?.lua'
  4626. myAPI=require'myFolder2.myAPI'
  4627. 2-directory named PACKAGE_NAME in _FILE_PATH, if defined in the caller's environment
  4628. with sufixes appended by @sufix and concatenated with @vars.paths.
  4629. _FILE_PATH is set, for instance, by lua_loader in the files it loads.
  4630. Example (_FILE_PATH='myFolder/myFolder3/myFolder/runningFile'):
  4631. PACKAGE_NAME='myFolder'
  4632. myAPI=require'myAPI' --@paths is 'myFolder/?;myFolder/?.lua;myFolder/?/init.lua;myFolder/?/?.lua;myFolder/?/?;myFolder'
  4633. 3-directory of _FILE_PATH, if defined
  4634. with sufixes appended by @sufix and concatenated with @vars.paths.
  4635. Example (_FILE_PATH='myFolder/runningFile'):
  4636. myAPI=require'myAPI' --@paths is 'myFolder/?;myFolder/?.lua;myFolder/?/init.lua;myFolder/?/?.lua;myFolder/?/?;myFolder'
  4637. [email protected] as set in loadreq.vars.paths
  4638. -replaces '.' in @s by '/' and '..' by '.'
  4639. --for all search_path in @paths
  4640. - for all iterators in vars.finders, iterates over the paths returned;
  4641. default iterator: see @direct
  4642. -for the first valid path, calls the loaders sequentially until one succeds,
  4643. in which case it returns the first value that the loader returns, else if it returns nil,e accumulates e as an error message
  4644. els, if all loaders fail, errors immediatly, printing all error messages
  4645. -in case of failure finding the path, errors with useful info
  4646. ]]
  4647.  
  4648. function require(s,opt)
  4649. opt=opt or {}
  4650. local t,e=_require(s,opt,getfenv(2))
  4651. if t==nil then
  4652. log('loadreq','ERROR','require:%s',e)
  4653. error(e,1)
  4654. else
  4655. log('loadreq','INFO','require: success in requiring [%s]',e)
  4656. return t
  4657. end
  4658. end
  4659.  
  4660. ---same as require, but copies the returned API to the caller's environment
  4661. function include(s,opt)
  4662. opt=opt or {}
  4663. local caller_env=getfenv(2)
  4664. local t,e=_require(s,opt,caller_env)
  4665. if t then
  4666. for i,v in pairs(t) do
  4667. if type(i)=='string' and i:sub(1,1)~='_' then
  4668. caller_env[i]=v
  4669. end
  4670. end
  4671. return true
  4672. else
  4673. log('loadreq','ERROR','include:%s',e)
  4674. error(e,1)
  4675. end
  4676. end
  4677.  
  4678. function reload(s,opt)
  4679. opt=opt or {}
  4680. opt.rerun=true
  4681. return require(s,opt)
  4682. end
  4683.  
  4684. --protection utilities; these are meant more as a warning than OS-grade protection
  4685. vars.bProtected=true
  4686. local function protected_access(t,k,v)
  4687. if vars.bProtected then
  4688. error( "Attempt to write to protected" )
  4689. else
  4690. rawset( t, k, v )
  4691. end
  4692. end
  4693. 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)
  4694. local meta = getmetatable( _t )
  4695. if meta == "Protected" then
  4696. -- already hard protected
  4697. return
  4698. end
  4699. if meta then
  4700. meta.__newindex = protected_access
  4701. else
  4702. setmetatable( _t, {__newindex = protected_access} )
  4703. end
  4704. end
  4705. function unprotect(_t)--removes the shallow protection
  4706. local meta = getmetatable( _t )
  4707. if not meta then
  4708. return
  4709. end
  4710. vars.bProtected=false
  4711. meta.__newindex=nil
  4712. vars.bProtected=true
  4713. end
  4714.  
  4715. function permaProtect(_t) --same as the os.loadAPI does
  4716. local meta = getmetatable( _t )
  4717. if meta == "Protected" then
  4718. -- already hard protected
  4719. return
  4720. end
  4721. setmetatable( _t, {
  4722. __newindex = function( t, k, v )
  4723. if vars.bProtected then
  4724. error( "Attempt to write to protected" )
  4725. else
  4726. rawset( t, k, v )
  4727. end
  4728. end,
  4729. __metatable = 'Protected',
  4730. } )
  4731. end
  4732.  
  4733. env=getfenv()
  4734. return env
  4735. ]===================],"CoolisOS/bin/kernel/log",true,"CoolisOS/bin/kernel/log/init.lua",[===================[------------------------------------------------------------------------------
  4736. -- Log library provides logging facilities.
  4737. -- The module exposes extension points. It is possible to provide both the custom printing function and the custom log saving function. <br />
  4738. -- The module is callable. Thus:
  4739. -- local log = require"log"
  4740. -- log("MODULE", "INFO", "message")
  4741. -- calls the @{#log.trace} function.
  4742. -- @module log
  4743. --
  4744. local string,pcall,table,tostring,setmetatable,os,pairs,next,print,math,type,unpack=
  4745. string,pcall,table,tostring,setmetatable,os,pairs,next,print,math,type,unpack
  4746. local base = _G
  4747.  
  4748. local util=require'kernel.util'
  4749. local check =require'kernel.checker'.check
  4750. local textutils=textutils
  4751. local string=string
  4752. --temporary
  4753. local pprint=pprint
  4754.  
  4755. --no more global access
  4756. local env=getfenv()
  4757. setmetatable(env,nil)
  4758.  
  4759. function s_to_real(t)
  4760. local n_s=t%60
  4761. local m=((t-n_s)/60)
  4762. local n_m=m%60
  4763. local h=((m-n_m)/60)
  4764. return h,n_m,n_s
  4765. end
  4766. local os_time= function()
  4767. local mc='Day '..os.day()..', '..textutils.formatTime(os.time())
  4768. local t=os.clock()
  4769. local h,n_m,n_s=s_to_real(t)
  4770. local sec=tostring(n_s)
  4771. local s,p,e=sec:match('^([^%.]*)(%.)([^%.]*)$')
  4772. s=s or '0'
  4773. p=p or '.'
  4774. e=e or '0'
  4775. s=string.rep('0',2-s:len())..s
  4776. e=e..string.rep('0',2-e:len())
  4777. sec=s..p..e
  4778. local real=string.format('%sh%sm%ss',h,n_m,sec)
  4779. return string.format('%s',sec)--string.format('%s;%s;%s',mc,real,sec)
  4780. end
  4781.  
  4782. -------------------------------------------------------------------------------
  4783. -- Log levels are strings used to filter logs.
  4784. -- Levels are to be used both in log filtering configuration (see @{#log.setLevel}) and each time
  4785. -- @{#log.trace} function is used to issue a new log.
  4786. --
  4787. -- Levels are ordered by verbosity/severity level.
  4788. --
  4789. -- While configuring log filtering, if you set a module log level to 'INFO' level for exemple, you
  4790. -- enable all logs *up to* 'INFO', that is to say that logs with 'WARNING' and 'ERROR' severities will
  4791. -- be displayed too.
  4792. --
  4793. -- Built-in values (in order from the least verbose to the most):
  4794. -- - 'NONE': filtering only: when no log is wanted
  4795. -- - 'ERROR': filtering or tracing level
  4796. -- - 'WARNING': filtering or tracing level
  4797. -- - 'INFO': filtering or tracing level
  4798. -- - 'DETAIL': filtering or tracing level
  4799. -- - 'DEBUG': filtering or tracing level
  4800. -- - 'ALL': filtering only: when all logs are to be displayed
  4801. -- @type levels
  4802. --
  4803. levels = {}
  4804. -- Severity name <-> Severity numeric value translation table (internal purpose only)
  4805. for k,v in pairs{ 'NONE', 'ERROR', 'WARNING', 'INFO', 'DETAIL', 'DEBUG', 'ALL' } do
  4806. levels[k], levels[v] = v, k
  4807. end
  4808.  
  4809. -- -----------------------------------------------------------------------------
  4810. -- Default verbosity level.
  4811. -- Default value is `"WARNING"`.
  4812. -- @field [parent=#log] #levels defaultlevel
  4813. -- See @{#log} for a list of existing levels.
  4814.  
  4815. local defaultlevel = defaultlevel or levels.WARNING
  4816.  
  4817. -- -----------------------------------------------------------------------------
  4818. -- Per module verbosity levels.
  4819. -- @field [parent=#log] modules
  4820. -- See @{#levels} to see existing levels.
  4821.  
  4822. local modules = modules or { }
  4823.  
  4824.  
  4825. -- -----------------------------------------------------------------------------
  4826. -- The string format of the timestamp is the same as what os.date takes.
  4827. -- Example: "%F %T"
  4828. -- #field [parent=#log] #string timestampformat
  4829. timestampformat = nil
  4830.  
  4831. -- -----------------------------------------------------------------------------
  4832. -- logger functions, will be called if non nil
  4833. -- the loggers are called with following params (module, severity, logvalue)
  4834. -- -----------------------------------------------------------------------------
  4835.  
  4836. -------------------------------------------------------------------------------
  4837. -- Default logger for instant display.
  4838. -- This logger can be replaced by a custom function.
  4839. -- It is called only if the log needs to be traced.
  4840. --
  4841. -- @function [parent=#log] displaylogger
  4842. -- @param #string module string identifying the module that issues the log
  4843. -- @param severity string representing the log level, see @{log#levels}.
  4844. -- @param msg string containing the message to log.
  4845. --
  4846. displaylogger = function(_, _, ...)
  4847. -- if base.print then base.print(...) end
  4848. end
  4849.  
  4850. -------------------------------------------------------------------------------
  4851. -- Logger function for log storage.
  4852. -- This logger can be replaced by a custom function.
  4853. -- There is no default store logger.
  4854. -- It is called only if the log needs to be traced (see @{#log.musttrace}) and after the log has been displayed using {displaylogger}.
  4855. --
  4856. -- @function [parent=#log] storelogger
  4857. -- @param module string identifying the module thats issues the log
  4858. -- @param severity string representing the log level, see @{log#levels}.
  4859. -- @param msg string containing the message to log.
  4860. --
  4861.  
  4862. storelogger = nil
  4863.  
  4864. -------------------------------------------------------------------------------
  4865. -- Format is a string used to apply specific formating before the log is outputted.
  4866. -- Within a format, the following tokens are available (in addition to standard text)
  4867. --
  4868. --- %l => the actual log (given in 3rd argument when calling log() function)
  4869. --- %t => the current date
  4870. --- %m => module name
  4871. --- %s => log level (severity), see @{log#levels}
  4872. --@field [parent=#log] #string format
  4873. format = nil
  4874.  
  4875. local function loggers(...)
  4876. if displaylogger then displaylogger(...) end
  4877. if storelogger then storelogger(...) end
  4878. end
  4879.  
  4880. -------------------------------------------------------------------------------
  4881. -- Determines whether a log must be traced depending on its severity and the
  4882. -- module issuing the log. This function is mostly useful to protect `log()`
  4883. -- calls which involve costly computations
  4884. --
  4885. -- @usage
  4886. --
  4887. -- if log.musttrace('SAMPLE', 'DEBUG') then
  4888. -- log('SAMPLE', 'DEBUG', "Here are some hard-to-compute info: %s",
  4889. -- computeAndReturnExpensiveDebugString())
  4890. -- end
  4891. --
  4892. -- @function [parent=#log] musttrace
  4893. -- @param modulename string identifying the module that issues the log.
  4894. -- @param severity string representing the log level, see @{log#levels}.
  4895. -- @return `nil' if the message of the given severity by the given module should
  4896. -- not be printed.
  4897. -- @return `true` if the message should be printed.
  4898. --
  4899. function musttrace(module, severity)
  4900. -- get the log level for this module, or default log level
  4901. local lev, sev = modules[module] or defaultlevel, levels[severity]
  4902. return not sev or lev >= sev
  4903. end
  4904.  
  4905. local function debug_getinfo(...)
  4906. return (base.debug and base.debug.getinfo or tostring)(...)
  4907. end
  4908. -------------------------------------------------------------------------------
  4909. -- Prints out a log entry according to the module and the severity of the log entry.
  4910. --
  4911. -- This function uses @{#log.format} and @{#log.timestampformat} to create the
  4912. -- final message string. It calls @{#log.displaylogger} and @{#log.storelogger}.
  4913. --
  4914. -- @function [parent=#log] trace
  4915. -- @param modulename string identifying the module that issues the log.
  4916. -- @param severity string representing the level in @{log#levels}.
  4917. -- @param fmt string format that holds the log text the same way as string.format does.
  4918. -- function must return fmt (string) , vararg
  4919. -- @param varargs additional arguments can be provided (as with string.format).
  4920. -- @usage trace("MODULE", "INFO", "message=%s", "sometext").
  4921. --
  4922. function trace(module, severity, fmt, ...)
  4923. local args
  4924. if type(fmt)=='function' then
  4925. local t={fmt(...)}
  4926. fmt=table.remove(t,1)
  4927. args=t
  4928. else args={...} end
  4929. check('string,string,string',module, severity, fmt)
  4930. if not musttrace(module, severity) then return end
  4931. 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)))
  4932. if c then
  4933. local t
  4934. local function sub(p)
  4935. if p=="l" then return s
  4936. elseif p=="t" then t = t or tostring(os_time(timestampformat)) return t
  4937. elseif p=="m" then return module
  4938. elseif p=="s" then return severity
  4939. else return p end
  4940. end
  4941. local out = (format or "%t %m-%s: %l"):gsub("%%(%a)", sub)
  4942. loggers(module, severity, out)
  4943. 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 !
  4944.  
  4945. --trace(module, severity, "\targs=("..table.concat(args, " ")..")" )
  4946. loggers(module, severity, "Error in the log formating! ("..tostring(s)..") - Fallback to raw printing:" )
  4947. loggers(module, severity, string.format("\tmodule=(%s), severity=(%s), format=(%q), args=(%s)", module, severity, fmt,table.concat(util.map_args(args,debug.getinfo),',') ))
  4948. end
  4949. end
  4950.  
  4951.  
  4952. -------------------------------------------------------------------------------
  4953. -- Sets the log level for a list of module names.
  4954. -- If no module name is given, the default log level is affected
  4955. -- @function [parent=#log] setLevel
  4956. -- @param slevel level as in @{log#levels}
  4957. -- @param varargs Optional list of modules names (string) to apply the level to.
  4958. -- @return nothing.
  4959. --
  4960. function setLevel(slevel, ...)
  4961. local mods = {...}
  4962. local nlevel = levels[slevel] or levels['ALL']
  4963. if not levels[slevel] then
  4964. trace("LOG", "ERROR", "Unknown severity %q, reverting to 'ALL'", tostring(slevel))
  4965. end
  4966. if next(mods) then for _, m in pairs(mods) do modules[m] = nlevel end
  4967. else defaultlevel = nlevel end
  4968. end
  4969.  
  4970. ---
  4971. local wrapped=setmetatable({},{__mode='k'})
  4972. function wrap_class(k)
  4973. assert(not wrapped[k])
  4974. local old=k.__init
  4975. rawset(k,'__init',function(...)
  4976. old(...)
  4977. env(tostring(k.__name),'DETAIL','initiated obj %s with args ',debug.getinfo(obj),args_tostring(...))
  4978. end)
  4979. wrapped[k]=old
  4980. end
  4981. function unwrapp_class(k)
  4982. if wrapped[k] then
  4983. rawset(k,'__init',wrapped[k])
  4984. wrapped[k]=nil
  4985. end
  4986. end
  4987. -- -----------------------------------------------------------------------------
  4988. -- Make the module callable, so the user can call log(x) instead of log.trace(x)
  4989. -- -----------------------------------------------------------------------------
  4990. setmetatable(env, {__call = function(_, ...) return trace(...) end })
  4991. return env
  4992.  
  4993. ]===================],"CoolisOS/bin/kernel/log/store.lua",[===================[local store={}
  4994. local log_dir=fs.combine(_INSTALL_PATH,'ect/log')
  4995. if not fs.exists(log_dir) then
  4996. fs.makeDir(log_dir)
  4997. end
  4998.  
  4999.  
  5000. local his={}
  5001.  
  5002. local function flush_file(s)
  5003. local f=fs.open(s,'a')
  5004. for i,v in pairs(his[s]) do
  5005. f.writeLine(v)
  5006. end
  5007. f.close()
  5008. his[s]=nil
  5009. end
  5010.  
  5011. function store.flush(module)
  5012. if not module then
  5013. for s,t in pairs(util.shcopy(his)) do
  5014. flush_file(s)
  5015. end
  5016. else
  5017. local s=fs.combine(log_dir,module..'.log')
  5018. flush_file(s)
  5019. end
  5020. end
  5021.  
  5022. store.instant=false
  5023. function store.append(module,msg)
  5024. local s=fs.combine(log_dir,module..'.log')
  5025. his[s]=his[s] or {}
  5026. table.insert(his[s],msg)
  5027. if store.instant==true then
  5028. flush_file(s)
  5029. end
  5030. end
  5031.  
  5032. function store.reset(module)
  5033. if module then
  5034. fs.open(fs.combine(log_dir,module..'.log'),'w').close()
  5035. else
  5036. fs.delete(log_dir)
  5037. fs.makeDir(log_dir)
  5038. end
  5039. end
  5040. log.storelogger=function(module,severity,msg)
  5041. store.append(module,msg)
  5042. store.append('log',msg)
  5043. end
  5044. return store]===================],"CoolisOS/bin/kernel/sched",true,"CoolisOS/bin/kernel/sched/barrier.lua",[===================[local check=require 'kernel.checker'.check
  5045.  
  5046. PACKAGE_NAME='sched'
  5047.  
  5048. local sched=require 'init'
  5049.  
  5050. --------------------------------------------------------------------------------
  5051. -- Object metatable/class
  5052. --------------------------------------------------------------------------------
  5053. local B={__type='barrier'};B.__index=B
  5054.  
  5055. --------------------------------------------------------------------------------
  5056. -- A registry of all currently active barriers. This is intended as a debug aid,
  5057. -- and can be safely commented out if not needed
  5058. --------------------------------------------------------------------------------
  5059. sched.barriers = setmetatable({}, {__mode='kv'})
  5060.  
  5061. function barrier(n)
  5062. check ('integer',n)
  5063. local instance={n=n}
  5064. if sched.barriers then
  5065. sched.barriers[tostring(instance):match(':.(.*)')]=instance
  5066. end
  5067. setmetatable(instance,B)
  5068. return instance
  5069. end
  5070.  
  5071. B.reach=function(self)
  5072. if self.n<=1 then
  5073. self.n=0
  5074. sched.signal(self,'zero')
  5075. else
  5076. self.n=self.n-1
  5077. sched.wait(self,'zero')
  5078. end
  5079. end
  5080.  
  5081. return barrier]===================],"CoolisOS/bin/kernel/sched/catalog.lua",[===================[--- A general purpose Catalog.
  5082. -- The catalog is used to give tasks well known names for sharing purposes.
  5083. -- It also allows synchronization, by blocking the requester until the object
  5084. -- is made available. Catalogs themselves are made available under a Well Known
  5085. -- name. Typical catalogs are "tasks", "mutexes" and "pipes".
  5086. -- The catalog does not check for multiple names per object.
  5087. -- @module catalog
  5088. -- @usage local tasks = require 'catalog'()
  5089. --...
  5090. --tasks:register('a task', sched.Task.running)
  5091. --...
  5092. --local a_task=tasks:waitFor('a task')
  5093. -- @alias M
  5094. include'kernel.class'
  5095. local log = require'kernel.log'
  5096. local check=require'kernel.checker'.check
  5097.  
  5098. local named=setmetatable({},{__mode='kv'})
  5099.  
  5100. class.Catalog()
  5101. local C=Catalog
  5102. C.named=named
  5103. function C:__init()
  5104. self.objs=setmetatable({},{__mode='kv'})
  5105. end
  5106. function C:register(obj,name)
  5107. if type(obj)~='table' then error('not a table',2) end
  5108. if self.objs[name]~=nil and self.objs[name]~=obj then error('name taken',2) end
  5109. self.objs[obj]=name
  5110. self.objs[name]=obj
  5111. sched.signal(self,'+'..name,obj)
  5112.  
  5113. named[obj]=named[obj] or setmetatable({},{__mode='kv'})
  5114. named[obj][self]=true
  5115. end
  5116. function C:unregister(id)
  5117. local s = self.objs[id]
  5118. local name=type(s)=='string' and s or id
  5119. self.objs[s]=nil
  5120. self.objs[id]=nil
  5121.  
  5122. if named[obj] then
  5123. named[obj][self]=nil
  5124. end
  5125. sched.signal(self,'-'..name,obj)
  5126. end
  5127. function C:waitFor(name,timeout)
  5128. check('string',name)
  5129. log('catalog', 'INFO', 'catalog %s queried for name "%s" by %s',self, name,sched.me())
  5130.  
  5131. local obj
  5132. if self[name] then return self[name] else
  5133. local em,ev,o=sched.wait(self,'+'..name,timeout)
  5134. if em==self then obj=o end
  5135. end
  5136. log('catalog', 'INFO', 'catalog %s queried for name "%s" by %s, found %s',self, name,sched.me(),obj)
  5137. return obj
  5138. end
  5139. function C:__tostring()
  5140. local t={}
  5141. for i,v in pairs(self.objs) do
  5142. if type(i)=='string' then t[i]=i..'->'..debug.getinfo(v) end
  5143. end
  5144. return debug.getinfo(self)..'\n'..pstring(t,1)
  5145. end
  5146.  
  5147. sched.Catalog=Catalog]===================],"CoolisOS/bin/kernel/sched/init.lua",[===================[include'kernel.class'
  5148. local log=require'kernel.log'
  5149. local check=require'kernel.checker'.check
  5150. local Linked=require 'utils.linked'
  5151. local table=table
  5152. local pairs=pairs
  5153.  
  5154. local sched,Obj,Sync,Task,timer,platform
  5155. local os_time
  5156.  
  5157. local fil
  5158. --[[
  5159. 2D array of sets of objs, fil[emitter][event][obj]=true
  5160. wildcards for emitter, event are '*'; can only be used for listening
  5161. reserved values; do not use for custom signals:
  5162. -emitters:
  5163. 'timer' -timer module;
  5164. 'platform' -events yielded to the scheduler by os.pullEventRaw
  5165. ]]
  5166.  
  5167.  
  5168. ---emits a signal with emitter @emitter, event @event and parameters @vararg (...)
  5169. --can be called recursively; be careful not to signal the same entry which is being run
  5170. --to use infinite loops of signals (e.g. A->B->A) blocking the scheduler, use @Task or @Call instances.
  5171. local signal = function (emitter,event,...)
  5172. log('sched', 'DEBUG', "SIGNAL [%s].[%s]", emitter, event)
  5173. local function walk_event(evl,emitter,event,...)
  5174. local copy={}
  5175. for obj,val in pairs(evl) do
  5176. copy[obj]=val
  5177. end
  5178. for obj,val in pairs(copy) do
  5179. -- if d then print(obj) end
  5180. obj:handle(emitter,event,...)
  5181. end
  5182. end
  5183.  
  5184. local function walk_emitter(eml,emitter,event,...)
  5185. local evl=eml[event]
  5186. if evl then
  5187. walk_event(evl,emitter,event,...)
  5188. end
  5189. evl=eml['*']
  5190. if evl then
  5191. walk_event(evl,emitter,event,...)
  5192. end
  5193. end
  5194.  
  5195. local eml=fil[emitter]
  5196. if eml then
  5197. walk_emitter(eml,emitter,event,...)
  5198. end
  5199. local eml=fil['*']
  5200. if eml then
  5201. walk_emitter(eml,emitter,event,...)
  5202. end
  5203.  
  5204. return true
  5205. end
  5206.  
  5207. --[[Class that Task,Sync implement and that describes what can be put into the waiting table @fil.
  5208. --Objs instances keep track of which signals call their @handle field in their @fil field;
  5209. --their @parent and @subs fields maintain a tree-like hierarchy used to destroy @subs from an object before destroying the object itself.
  5210. --Objs expect to have defined a handle field consisting of a function used to process signals received, syncronously:
  5211. --@handle is a function called with args (obj,emitter,event,...) everytime a signal (emitter,event,...) that obj is linked too is emitted.
  5212. ]]
  5213. Obj=class('Obj')
  5214. do
  5215. local O=Obj
  5216. --@name is an optional parameter to set a debugging name
  5217. function O:__init()
  5218. self.fil={}
  5219. self.subs={}
  5220. if sched.running then self:setParent(sched.running) end
  5221. end
  5222.  
  5223. ---Takes a link descriptor and tranforms them into a a proper link table for Obj methods to consume
  5224. --supports emitter,ev1,ev2,...,timeout
  5225. --or {[emt1]={ev1,ev2,...},...},timeout
  5226. local function get_args(...)
  5227. local args={...}
  5228. local nargs=#args
  5229. local t,timeout
  5230. if type(args[nargs])=='number' then
  5231. timeout=args[nargs]
  5232. nargs=nargs-1
  5233. end
  5234. if nargs~=0 then
  5235. if nargs==1 and typeof(args[1])=='table' then
  5236. t=args[1]
  5237. else
  5238. t={[args[1]]={nargs==1 and '*' or unpack(args,2,nargs)}}
  5239. end
  5240. end
  5241. return t,timeout
  5242. end
  5243.  
  5244. --helper
  5245. local add_timer=function(t,nd)
  5246. t=t or {}
  5247. if nd then
  5248. if not t[timer] then
  5249. t[timer]={nd}
  5250. else
  5251. table.insert(t[timer],nd)
  5252. end
  5253. end
  5254. return t
  5255. end
  5256.  
  5257. function get_t(...)
  5258. local t,timeout=get_args(...)
  5259. if timeout then t=add_timer(t,timeout+os_time()) end
  5260. return t
  5261. end
  5262.  
  5263. ---Registers @obj in the waiting table @fil in the waiting sets described by the link table @t
  5264. function O:link(...)
  5265. local t=get_t(...)
  5266. if t==nil then return end
  5267. local ofil=self.fil
  5268. local root=self.__root
  5269. local fil_tev,ofil_tev,fil_tobj
  5270. for em,tev in pairs(t) do
  5271. if type(tev)~='table' then error('Wrong struct for link',2) end
  5272. fil_tev=fil[em]
  5273. ofil_tev=ofil[em]
  5274. if not ofil_tev then
  5275. ofil[em]={}
  5276. ofil_tev=ofil[em]
  5277. if not fil_tev then fil[em]={} fil_tev=fil[em] end
  5278. end
  5279. local ev
  5280. for i=1,#tev do
  5281. ev=tev[i]
  5282. ofil_tev[ev]=true
  5283. fil_tobj=fil_tev[ev]
  5284. if not fil_tobj then fil_tev[ev]={} fil_tobj=fil_tev[ev] end
  5285. fil_tobj[root]=true
  5286. end
  5287. end
  5288. end
  5289.  
  5290. ---Unregisters @obj in the waiting table @fil from the waiting sets described by the link table @t
  5291. function O:unlink(...)
  5292. local r=self.__root
  5293. local t=get_t(...)
  5294. if t==nil then return end
  5295. local ofil=self.fil
  5296. local fil_tev,ofil_tev,fil_tobj
  5297. for em,tev in pairs(t) do
  5298. ofil_tev=ofil[em]
  5299. if ofil_tev then
  5300. fil_tev=fil[em]
  5301. local ev
  5302. for i=1,#tev do
  5303. ev=tev[i]
  5304. if ofil_tev[ev] then
  5305. ofil_tev[ev]=nil
  5306.  
  5307. fil_tobj=fil_tev[ev]
  5308. fil_tobj[r]=nil
  5309. if not next(fil_tobj) then fil_tev[ev]=nil end
  5310. end
  5311. end
  5312. if not next(ofil_tev) then ofil[em]=nil end
  5313. if not next(fil_tev) then fil[em]=nil end
  5314. end
  5315. end
  5316. end
  5317.  
  5318. ---Unregisters @obj from all the waiting sets it is linked to.
  5319. function O:reset()
  5320. local fil_tev
  5321. local r=self.__root
  5322. for em,tev in pairs(self.fil) do
  5323. fil_tev=fil[em]
  5324. if fil_tev then
  5325. for ev in pairs(tev) do
  5326. -- print(ev,'|',self,'|',fil_tev[ev],'|',fil_tev[ev][self])
  5327. if fil_tev[ev] then
  5328. fil_tev[ev][r]=nil
  5329. if not next(fil_tev[ev]) then fil_tev[ev]=nil end
  5330. end
  5331. end
  5332. if not next(fil_tev) then fil[em]=nil end
  5333. end
  5334. end
  5335. self.fil={}
  5336. end
  5337.  
  5338. local ending={n=0}
  5339. --helper
  5340. ---Kills @obj 's subs and removes it from the waiting sets.
  5341. function O:silent_destroy()
  5342. local root=self.__root
  5343. if ending[root] then
  5344. error('Detected recursion in Obj.finalize: ending stack='..pstring(ending),2)
  5345. else
  5346. ending.n=ending.n+1
  5347. ending[root]=ending.n
  5348. end
  5349. --handle subs
  5350. while next(self.subs) do
  5351. next(self.subs):destroy()
  5352. end
  5353.  
  5354. if self.parent then
  5355. self.parent.Obj.subs[root]=nil
  5356. end
  5357. --remove self from the waiting sets
  5358. self:reset()
  5359.  
  5360. --remove self from the action queue
  5361. sched.queue:remove(root)
  5362.  
  5363. ending[root]=nil
  5364. ending.n=ending.n-1
  5365. end
  5366.  
  5367. ---same as @O:silent_destroy, but throws some useful warning signals.
  5368. function O:destroy(...)
  5369. if not self.destroyed then
  5370. local r=self.__root
  5371. signal(r,'dying',...) --warns subs
  5372. self:silent_destroy()
  5373. signal(r,'dead',...)
  5374. self.destroyed=true
  5375. end
  5376. end
  5377.  
  5378. local function check_tree(self,new_parent,seen)
  5379. if new_parent==nil then return true,seen end
  5380. new_parent=new_parent.__root
  5381. local r=self.__root
  5382. seen=seen or {r,[r]=true}
  5383. if seen[new_parent] then return false,seen end
  5384. table.insert(seen,new_parent)
  5385. seen[new_parent]=true
  5386. return check_tree(new_parent,new_parent.Obj.parent,seen)
  5387. end
  5388. ---Changes an @obj parent field.
  5389. function O:setParent(parent)
  5390. local rparent,oparent=parent.__root,parent.Obj
  5391. local root=self.__root
  5392. local ok, t=check_tree(self,self.parent)
  5393. if not ok then error('Cycle detected in scheduler hierarchy;debug:'..pstring(t),2) end
  5394. if rparent==root then error("A scheduler object cannot be it's own parent",2) end
  5395. if self.parent then self.parent.Obj.subs[root]=nil end
  5396. self.parent=rparent
  5397. oparent.subs[root]=true
  5398. log('sched','DETAIL','(%s) parent set to (%s) by (%s)',self.__root,(parent and parent.__root),sched.running or 'nil')
  5399. end
  5400.  
  5401. local TIMEOUT_TOKEN={}
  5402. ---helpers for linking @obj to timer signals
  5403. function O:setTimeout(timeout)
  5404. if type(timeout)~='number' then
  5405. error('timeout must be number',2)
  5406. else
  5407. timeout=timer.norm(timeout)
  5408. end
  5409. self:cancelTimeout()
  5410. self[TIMEOUT_TOKEN]={
  5411. timeout=timeout,
  5412. td={timer={timeout+os_time()}},
  5413. }
  5414. self:link(self[TIMEOUT_TOKEN].td)
  5415. return self
  5416. end
  5417.  
  5418. ---cancels the current timeout and sets a new one in @timeout seconds, where @timeout was set by @O:setTimeout
  5419. function O:resetTimeout()
  5420. local td=self[TIMEOUT_TOKEN]
  5421. if td then
  5422. self:unlink(td.td)
  5423. td.td[timer][1]=os_time()+td.timeout
  5424. self:link(self.td.td)
  5425. end
  5426. end
  5427.  
  5428. ---cancels the current timeout
  5429. function O:cancelTimeout()
  5430. local td=self[TIMEOUT_TOKEN]
  5431. if td then
  5432. self:unlink(td.td)
  5433. self.td=nil
  5434. end
  5435. end
  5436.  
  5437. function O:__debug()
  5438. local fil={}
  5439. for i,v in pairs(self.fil) do
  5440. local k=debug.getinfo(i)
  5441. fil[k]={}
  5442. for j in pairs(v) do
  5443. table.insert(fil[k],debug.getinfo(j))
  5444. end
  5445. end
  5446. return string.format('%s; \nWait descriptor:%s',debug.objinfo(self),pstring(fil))
  5447. end
  5448. end
  5449.  
  5450. --[[syncronous calls; are called as soon as the signal is received, but can't block as tasks do
  5451. once are one-use,
  5452. on are permanent,
  5453. ]]
  5454. Sync=class('Sync',Obj)
  5455. do
  5456. local S=Sync
  5457. local sync_once_handle = function(self,...)
  5458. local sync=self.Sync
  5459. local running=sched.running
  5460. sched.running=self
  5461. sync.f(...)
  5462. self:destroy()
  5463. sched.running = running
  5464. end
  5465. local sync_on_handle=function(self,...)
  5466. local running=sched.running
  5467. sched.running=self
  5468. local sync=self.Sync
  5469. sync.f(...)
  5470. self:resetTimeout()
  5471. sched.running=running
  5472. end
  5473. function S:__init(f,mode)
  5474. check('function',f)
  5475. mode=mode or 'on'
  5476. Obj:__init(self)
  5477. if mode=='once' then
  5478. elseif mode=='on' then
  5479. else error('mode?',2) end
  5480. self.f=f
  5481. self.mode=mode
  5482. log('sched','INFO','(%s) created with mode %s by (%s)',self,mode,sched.running or 'nil')
  5483. end
  5484. function S:handle(...)
  5485. if self.mode=='on' then
  5486. sync_on_handle(self,...)
  5487. else
  5488. sync_once_handle(self,...)
  5489. end
  5490. end
  5491. end
  5492. --asyncronous calls through coroutines
  5493. Task=class('Task',Obj)
  5494. do
  5495. local T=Task
  5496. ---Creates a task object in paused mode;
  5497. ---fields set by Task.new are
  5498. --co: matching coroutine
  5499. --args: table of args to unpack and pass to co
  5500. --parent: parent task
  5501. --subs: sub tasks (see @{Task.setSub} and @{Task.setParent}
  5502. --name: name used for logging and result of tostring(task)
  5503. --Can take either (f) or (name,f) as args
  5504. --@return task
  5505. function T:__init(f,...)
  5506. check('function',f)
  5507. Obj:__init(self)
  5508. self.co=coroutine.create( f )
  5509. self._resume=function(co)
  5510. s,m=coroutine.resume(self.co,unpack(self.args))
  5511. self._resume=coroutine.resume
  5512. return s,m
  5513. end
  5514. self.args={...}
  5515. log('sched', 'INFO', 'created (%s) from function (%s) by (%s)', self, f,sched.running or 'nil')
  5516. end
  5517.  
  5518. ---Schedules a task for execution with args (...)
  5519. --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.
  5520. function T:handle(...)
  5521. self.args={...} --no point in catching args after nil, since self.args is unpacked before returning to the task.
  5522. sched.queue:push(self)
  5523. sched.ready=true
  5524.  
  5525. log('sched', 'DETAIL', 'Task:handle rescheduling (%s) to receive SIGNAL [%s].[%s]',
  5526. self,...)
  5527. return self
  5528. end
  5529.  
  5530. ---Runs a task with initial args ...
  5531. --@param vararg optional arguments to start the task with
  5532. --@return task
  5533. function T:run(...)
  5534. self.args={...}
  5535. sched.queue:push(self)
  5536. log('sched', 'INFO', "Task.run scheduling (%s)", self)
  5537. return self
  5538. end
  5539.  
  5540. --- Finishes a task and destroys it's subs.
  5541. -- @param self task to terminate (see @{Task.new})
  5542. -- @return task
  5543. function T:destroy()
  5544. if self.status~='dead' then
  5545. log('sched', 'INFO', "Task.destroy destroying (%s) from (%s)",self,sched.running or 'nil')
  5546. self.Obj:destroy()
  5547. self.status='dead'
  5548. if Task.running and Task.running.status=='dead' and Task.running.co==coroutine.running() then
  5549. coroutine.yield()
  5550. end
  5551. end
  5552. return self
  5553. end
  5554.  
  5555. function T:resume()
  5556. sched.running = self
  5557.  
  5558. local co=self.co
  5559. Task.running=self
  5560.  
  5561. log('sched', 'DETAIL', "Resuming (%s)", self)
  5562. local success, msg = self._resume(co)
  5563. log('sched', 'DETAIL', "Exited (%s)", self)
  5564. Task.running=nil
  5565. self.args={}
  5566. sched.running = nil
  5567. if not success then
  5568. -- report the error msg
  5569. log('sched', 'ERROR', "In (%s):%s",self,msg)
  5570. signal(self, "error",success, msg)
  5571. --preserve events/subs for error catchers to analize, and then finalize
  5572. self:destroy(success,msg)
  5573. elseif coroutine.status (co) == "dead" then --If the coroutine died, signal it for those who synchronize on its termination.
  5574. log('sched', 'INFO', "(%s) is dead", self)
  5575. self:destroy(success,msg)
  5576. end
  5577. end
  5578.  
  5579. --- Waits for a signal
  5580. --Can only be called from a task
  5581. --If called with no arguments, yields and reschedules for execution
  5582. --If called with false as argument, yields without rescheduling for execution
  5583. -- @param vararg an entries descriptor for the signal (see @{get_args})
  5584. -- @return emitter, event, parameters
  5585. function T:wait(...)
  5586. local task = self
  5587. if task.co~=coroutine.running() then error('calling Task.wait outside a task/inside a task but inside another coroutine',2) end
  5588. if (...)==nil then
  5589. sched.queue:push(task)
  5590. log('sched', 'DETAIL', "Task.wait rescheduling (%s) for resuming ASAP", task)
  5591. elseif ... then
  5592. task:link(...)
  5593. log('sched', 'DETAIL', "(%s) waiting", task)
  5594. else
  5595. log('sched', 'DETAIL', "(%s) waiting for pre-set signals", task)
  5596. end
  5597. coroutine.yield ()
  5598. if ... then
  5599. task:reset()
  5600. end
  5601. return unpack(task.args)
  5602. end
  5603.  
  5604. ---resets the Task class vars
  5605. function Task._reset()
  5606. Task.running=nil
  5607. end
  5608.  
  5609. ---Some pre-built utilities for tasks
  5610. --- Create a task that listens for a signal.
  5611. -- @param f function to be called when the signal appears. The signal
  5612. -- is passed to f as parameter. The signal will be provided as
  5613. -- emitter, event, parameters, just as the result of a @{wait}
  5614. -- @param vararg a Wait Descriptor for the signal (see @{get_args})
  5615. -- @return task in the scheduler
  5616. Task.sigrun= function (f,...)
  5617. local ff=function()
  5618. while true do
  5619. f(sched.wait(false))
  5620. end
  5621. end
  5622. local t=Task(ff)
  5623. t:link(...)
  5624. return t
  5625. end
  5626. --- Create a task that listens for a signal, once.
  5627. -- @param f function to be called when the signal appears. The signal
  5628. -- is passed to f as parameter. The signal will be provided as
  5629. -- _emitter, event, parameters_, just as the result of a @{wait}
  5630. -- @param vararg a Wait Descriptor for the signal (see @{get_args})
  5631. -- @return task in the scheduler
  5632. Task.sigrunonce= function (f,...)
  5633. local ff=function()
  5634. f(sched.wait(false))
  5635. end
  5636. local t=Task(ff)
  5637. t:link(...)
  5638. return t
  5639. end
  5640. end
  5641.  
  5642.  
  5643. do
  5644. sched={
  5645. --modules and respective shortcuts
  5646. signal=signal,
  5647. emit=function(...)
  5648. sched.signal(sched.me(),...)
  5649. end,
  5650.  
  5651. Obj=Obj,
  5652. ready=false,
  5653. me=function() return sched.running end,
  5654. Task=Task,
  5655. Sync=Sync,
  5656. -- Call=Call,
  5657. -- Wait=Wait,
  5658.  
  5659. sigonce=function(f) return Sync(f,'once') end,
  5660. sighook=function(f) return Sync(f,'on') end,
  5661. wait=function(...) return sched.running:wait(...) end,
  5662. sigrun=Task.sigrun,
  5663. sigrunonce=Task.sigrunonce,
  5664.  
  5665. --others
  5666. }
  5667. local _require=function(p,t)
  5668. t=t or{}
  5669. t.env={sched=sched}
  5670. return require(p,t)
  5671. end
  5672. platform=_require'platform'
  5673.  
  5674. os_time=platform.time
  5675.  
  5676. timer=_require'timer'
  5677.  
  5678. _require'pipe'
  5679. _require'catalog'
  5680.  
  5681.  
  5682.  
  5683. local loop_state = 'stopped' -- stopped, running or stopping
  5684.  
  5685. function sched.step()
  5686. local old = sched.queue
  5687. sched.queue=Linked()
  5688. --------------------------------------------------------------------
  5689. -- going through `self.queue` until it's empty.
  5690. --------------------------------------------------------------------
  5691. while true do
  5692. local obj = old:remove() --pops first from left->right
  5693. if obj==nil then break end
  5694.  
  5695. obj:resume()
  5696. end
  5697. if not sched.queue:isempty() then sched.ready=true end
  5698. end
  5699.  
  5700. ---Exits the scheduler after the current cycle is completed.
  5701. function sched.stop()
  5702. log('sched','INFO','(%s) toggling loop_state=%s to stopping',Task.running or 'scheduler',loop_state)
  5703. if loop_state=='running' then loop_state = 'stopping' end
  5704. end
  5705.  
  5706. ---resets internal vars
  5707. function sched.reset()
  5708. fil={}
  5709. sched.fil=fil
  5710. sched.running=nil
  5711. sched.queue=Linked()
  5712. Task._reset()
  5713. timer._reset()
  5714.  
  5715. platform._reset() --after timer
  5716. loop_state = 'stopped'
  5717. log('sched','INFO','scheduler cleaned.')
  5718. end
  5719.  
  5720. ---Loops over the scheduler cycle,
  5721. --returning to the caller function after sched.stop has been called
  5722. --calling platform.step for performing sleeps.
  5723. function sched.loop ()
  5724. log('sched','INFO','Scheduler started')
  5725. loop_state = 'running'
  5726. local timeout
  5727. while true do --this block is the scheduler step
  5728. timer.step() -- Emit timer signals
  5729. sched.step() -- Run all the ready tasks
  5730. -- Find out when the next timer event is due
  5731. timeout = nil
  5732. local date = timer.nextevent()
  5733. if date then
  5734. local now=os_time()
  5735. timeout = date<now and 0 or date-now
  5736. end
  5737.  
  5738. if loop_state~='running' then sched.reset() break end
  5739. --debug.log(sched.debug())
  5740. platform.step(timeout) -- Wait for platform events until the next timer is due
  5741. sched.ready=false
  5742. end
  5743. end
  5744.  
  5745. function sched.debug()
  5746. local d={}
  5747. local t={}
  5748. for em,emt in pairs(fil) do
  5749. local tt={}
  5750. for ev,objt in pairs(emt) do
  5751. local ttt={}
  5752. for obj in pairs(objt) do
  5753. table.insert(ttt,debug.getinfo(obj))
  5754. end
  5755. tt[debug.getinfo(ev)]=ttt
  5756. end
  5757. t[debug.getinfo(em)]=tt
  5758. end
  5759. d.fil=t
  5760. return pstring(d)
  5761. end
  5762.  
  5763. local t={}
  5764. for i,v in pairs(sched) do
  5765. if type(i)=='string' then t[i:lower()]=v end
  5766. end
  5767. for i,v in pairs(t) do sched[i]=v end
  5768.  
  5769. sched.reset()
  5770. end
  5771. return sched]===================],"CoolisOS/bin/kernel/sched/lock.lua",[===================[--------------------------------------------------------------------------------
  5772. -- Thread-bound lock objects
  5773. -- =========================
  5774. --
  5775. -- A lock allows to define atomic units of code execution:
  5776. -- a portion of code that is protected by a lock will never be executed by more
  5777. -- than one thread at a time.
  5778. -- Because of the use of coroutines for the threading in Lua, the use of locks
  5779. -- is only relevant when you want to protect a portion of code that contains
  5780. -- blocking APIs (i.e. network access, scheduling, etc...)
  5781. --
  5782. -- A lock must be acquired and released by the same thread: an error will be
  5783. -- triggered if a thread tries to release a lock acquired by another thread.
  5784. -- This behavior is enforced because it is almost always a concurrency mistake
  5785. -- to have a thread releasing another thread's locks; but you can circumvent
  5786. -- this by passing the owning thread as an extra parameter.
  5787. --
  5788. -- A lock is automatically released when its owning thread dies, if it failed
  5789. -- to do so explicitly.
  5790. --
  5791. -- API
  5792. -- ---
  5793. --
  5794. -- lock.new()
  5795. -- return a new instance of a lock.
  5796. --
  5797. -- LOCK :acquire ()
  5798. -- acquire the lock.
  5799. --
  5800. -- LOCK :release ([thread])
  5801. -- release the lock. The optional thread param is defaulted to the
  5802. -- current thread. A lock must be released by the thread that
  5803. -- acquired it (or use the thread params to release another thread's lock).
  5804. --
  5805. -- LOCK :destroy()
  5806. -- destroy the lock. Any waiting thread on that lock will trigger a
  5807. -- "destroyed" error
  5808. --
  5809. --------------------------------------------------------------------------------
  5810.  
  5811. --------------------------------------------------------------------------------
  5812. -- Associative lock objects
  5813. -- ------------------------
  5814. --
  5815. -- Function lock (object) allows to associate a lock to an arbitrary object.
  5816. -- This API creates a standard lock (as above). It removes the burden of
  5817. -- creating the lock and managing the association between the object
  5818. -- and the lock.
  5819. --
  5820. -- Locks associated to objects still have an owning thread, and the rule
  5821. -- "locks must be released by the thread which owns them" still applies.
  5822. --
  5823. -- API
  5824. --
  5825. -- lock.lock (object)
  5826. -- lock the object.
  5827. --
  5828. -- lock.unlock(object, thread)
  5829. -- unlock the object. thread is optional as in lock:acquire() function.
  5830. --
  5831. --------------------------------------------------------------------------------
  5832.  
  5833. --------------------------------------------------------------------------------
  5834. -- Synchronized functions
  5835. -- ----------------------
  5836. --
  5837. -- A common pattern is to have a function that must not be called more than once
  5838. -- at a given time: if the function is already running in another thread, one
  5839. -- must wait for this call to finish before calling it again.
  5840. --
  5841. -- API
  5842. --
  5843. -- lock.synchronized (f)
  5844. -- return a synchronized wrapper around f. A typical idiom is:
  5845. --
  5846. -- mysynchronizedfunction = lock.synchronized (function (...)
  5847. -- [code]
  5848. -- end)
  5849. --------------------------------------------------------------------------------
  5850. include'kernel.class'
  5851. local check = require 'kernel.checker'.check
  5852.  
  5853. local sched = require 'init'
  5854.  
  5855. local setmetatable = setmetatable
  5856. local tostring = tostring
  5857. local string = string
  5858. local error = error
  5859. local assert = assert
  5860. local table = table
  5861. local next = next
  5862. local pairs = pairs
  5863. local type = type
  5864. local unpack = unpack
  5865.  
  5866. env=getfenv()
  5867. setmetatable(env,nil)
  5868.  
  5869. --------------------------------------------------------------------------------
  5870. --
  5871. --------------------------------------------------------------------------------
  5872. class.LOCK()
  5873. LOCK.hooks={}
  5874. LOCK.objlocks=setmetatable({}, {__mode = "k"})
  5875. --------------------------------------------------------------------------------
  5876. -- Create a new lock object. It remains unlocked.
  5877. --------------------------------------------------------------------------------
  5878. setmetatable(env,{__call=function() return LOCK(...) end})
  5879. function LOCK:__init()
  5880. self.waiting={}
  5881. end
  5882. --------------------------------------------------------------------------------
  5883. -- Destroy a lock object.
  5884. --------------------------------------------------------------------------------
  5885. function LOCK:destroy()
  5886. self.owner = "destroyed" -- means that the lock is being destroyed !
  5887. for t, _ in pairs(self.waiting) do
  5888. sched.signal(self, t)
  5889. self.waiting[t] = nil
  5890. end
  5891. end
  5892.  
  5893. --------------------------------------------------------------------------------
  5894. -- Helper to release locks on dead threads.
  5895. --------------------------------------------------------------------------------
  5896. local function autorelease(thread)
  5897. for l, _ in pairs(LOCK.hooks[thread]) do
  5898. if l.owner == thread then l:release(thread)
  5899. elseif l.waiting then l.waiting[thread] = nil end
  5900. end
  5901. LOCK.hooks[thread] = nil
  5902. end
  5903.  
  5904. --------------------------------------------------------------------------------
  5905. -- Helper to release locks on dead threads.
  5906. --------------------------------------------------------------------------------
  5907. local function protectdie(self, thread)
  5908. if not LOCK.hooks[thread] then
  5909. local h = sched.sigonce(function()
  5910. autorelease(thread)
  5911. end,thread, "dead")
  5912. LOCK.hooks[thread] = {sighook = h}
  5913. end
  5914. (LOCK.hooks[thread])[self] = true
  5915. end
  5916.  
  5917. --------------------------------------------------------------------------------
  5918. -- Helper to release locks on dead threads.
  5919. --------------------------------------------------------------------------------
  5920. local function unprotectdie(self, thread)
  5921. (LOCK.hooks[thread])[self] = nil
  5922. if not next(LOCK.hooks[thread], next(LOCK.hooks[thread])) then -- if this was the last lock attached to that thread...
  5923. (LOCK.hooks[thread].sighook):destroy()
  5924. LOCK.hooks[thread] = nil
  5925. end
  5926. end
  5927.  
  5928. --------------------------------------------------------------------------------
  5929. -- Attempt to take ownership of a lock; might block until the current owner
  5930. -- releases it.
  5931. --------------------------------------------------------------------------------
  5932. function LOCK:acquire()
  5933. local t = sched.task.running
  5934. assert(self.owner ~= t, "a lock cannot be acquired twice by the same thread")
  5935. assert(self.owner ~= "destroyed", "cannot acquire a destroyed lock")
  5936. protectdie(self, t) -- ensure that the lock will be unlocked if the thread dies before unlocking...
  5937.  
  5938. while self.owner do
  5939. self.waiting[t] = true
  5940. sched.wait(self,t) -- wait on the current lock with the current thread
  5941. if self.owner == "destroyed" then error("lock destroyed while waiting") end
  5942. end
  5943. self.waiting[t] = nil
  5944. self.owner = t
  5945. end
  5946.  
  5947. --------------------------------------------------------------------------------
  5948. -- Release ownership of a lock.
  5949. --------------------------------------------------------------------------------
  5950. function LOCK:release(thread)
  5951. thread = thread or sched.task.running
  5952. assert(self.owner ~= "destroyed", "cannot release a destroyed lock")
  5953. assert(self.owner == thread, "unlock must be done by the thread that locked")
  5954. unprotectdie(self, thread)
  5955. self.owner = nil
  5956.  
  5957. -- wakeup a waiting thread, if any...
  5958. local t = next(self.waiting)
  5959. if t then
  5960. sched.signal(self, t)
  5961. end
  5962. end
  5963.  
  5964. --------------------------------------------------------------------------------
  5965. -- Create and acquire a new lock, associated to an arbitrary object.
  5966. --------------------------------------------------------------------------------
  5967. function lock(object)
  5968. assert(object, "you must provide an object to lock on")
  5969. assert(type(object) ~= "string" and type(object) ~= "number", "the object to lock on must be a collectable object (no string or number)")
  5970. if not LOCK.objlocks[object] then LOCK.objlocks[object] = new() end
  5971. LOCK.objlocks[object]:acquire()
  5972. end
  5973.  
  5974.  
  5975. --------------------------------------------------------------------------------
  5976. -- Release an object created with lock().
  5977. --------------------------------------------------------------------------------
  5978. function unlock(object, thread)
  5979. assert(object, "you must provide an object to unlock on")
  5980. assert(LOCK.objlocks[object], "this object was not locked")
  5981. LOCK.objlocks[object]:release()
  5982. end
  5983.  
  5984. --------------------------------------------------------------------------------
  5985. -- Create a synchronized version of function f, i.e. a function that behaves
  5986. -- as f, except that no more than one instance of it will be running at a
  5987. -- given time.
  5988. --------------------------------------------------------------------------------
  5989. function synchronized(f)
  5990. check(f,'function')
  5991. local function sync_f(...)
  5992. local k = lock (f)
  5993. local r = {f(...)}
  5994. unlock(f)
  5995. return unpack (r, 1, r.n)
  5996. end
  5997. return sync_f
  5998. end
  5999.  
  6000. return env
  6001. ]===================],"CoolisOS/bin/kernel/sched/pipe.lua",[===================[--------------------------------------------------------------------------------
  6002. -- FIFO PIPES
  6003. -- ==========
  6004. --
  6005. -- Purpose
  6006. -- -------
  6007. -- Tasks can't easily exchange messages with sighook() or wait():
  6008. --
  6009. -- Calling wait() in a loop might lose some messages, if a message arrives
  6010. -- before the loop had the time to go back to wait(). Example:
  6011. --
  6012. -- ----------------------------------------------------------
  6013. -- -- sender --
  6014. -- for i=1, 100 do signal('foo', 'tick', i); sched.wait (2) end
  6015. --
  6016. -- -- receiver --
  6017. -- while true do
  6018. -- local _, n = sched.wait('foo', 'tick')
  6019. -- printf ("received %i", n)
  6020. -- sched.wait(3)
  6021. -- end
  6022. -- ----------------------------------------------------------
  6023. --
  6024. -- sighooks are guaranteed to be called everytime the event is triggered,
  6025. -- but they are executed as interruptions, they can't pause a task.
  6026. --
  6027. -- Pipes solve this issue: everything that is written in a pipe with :send()
  6028. -- will be read back, in order, with :receive(). If there's nothing to read yet,
  6029. -- the task calling :receive() will pause and wait for the next :send() to
  6030. -- provide data to read back.
  6031. --
  6032. -- :receive() can yield, and therefore must not be used in a sighook.
  6033. --
  6034. -- :send() can yield only if there is a maxlength and the pipe is already full.
  6035. --
  6036. --
  6037. -- API
  6038. -- ---
  6039. -- - create a new pipe with:
  6040. -- P = pipe()
  6041. -- - push data in it with:
  6042. -- P :send(x). x can't be nil.
  6043. -- - read data back with:
  6044. -- P :receive() or P :receive(timeout).
  6045. -- This call will block if necessary. It also supports an optional timeout
  6046. -- parameter. If there is a timeout and nothing can be read within the delay,
  6047. -- nil is returned.
  6048. -- - do a non-blocking, non-consumming peek at next value with:
  6049. -- P :peek()
  6050. -- This might return nil is the pipe is currently empty.
  6051. -- - return all of the pipe's current content and reset it as empty with:
  6052. -- P :reset()
  6053. -- - cancel a reading by pushing back an element in the pipe with:
  6054. -- P :pushback(x)
  6055. -- - retrieve the number of elements currently in a pipe with:
  6056. -- P :length()
  6057. -- - tune the recompaction parameters:
  6058. -- P :setwaste(absolute_waste_factor, proportional_waste_factor)
  6059. --
  6060. --
  6061. -- Algorithmic complexity
  6062. -- ----------------------
  6063. -- The amortized complexity for sending and receiving is o(log(N)), N
  6064. -- being the current number of elements in the pipe. Notice that with
  6065. -- the obvious/naive implentation based on table.insert() and
  6066. -- table.remove(), one of the operations is in o(log(N)) but the other
  6067. -- is in o(N); therefore, writing K elements then reading them back
  6068. -- would have a time complexity in o(K^2), compared to o(K*log(K)) here.
  6069. --
  6070. --------------------------------------------------------------------------------
  6071. include'kernel.class'
  6072.  
  6073. local check=require 'kernel.checker'.check
  6074.  
  6075. local sched=sched
  6076.  
  6077. --------------------------------------------------------------------------------
  6078. -- Object metatable/class
  6079. --------------------------------------------------------------------------------
  6080.  
  6081. sched.Pipe=class'Pipe'
  6082. do
  6083. local P=sched.Pipe
  6084.  
  6085. local insert, remove, os_time = table.insert, table.remove, os.clock
  6086.  
  6087. --------------------------------------------------------------------------------
  6088. -- A registry of all currently active pipes. This is intended as a debug aid,
  6089. -- and can be safely commented out if not needed
  6090. --------------------------------------------------------------------------------
  6091. sched.pipes = setmetatable({}, {__mode='kv'})
  6092.  
  6093. --------------------------------------------------------------------------------
  6094. -- Create a new pipe
  6095. --------------------------------------------------------------------------------
  6096.  
  6097. function P:__init(maxlength)
  6098. check ('?number',maxlength)
  6099. util.shcopy({
  6100. sndidx = 1;
  6101. rcvidx = 1;
  6102. content = { };
  6103. maxlength = maxlength;
  6104. state = 'empty';
  6105. wasteabs = 32 ;
  6106. wasteprop = 2 },
  6107. self)
  6108. if sched.pipes then
  6109. sched.pipes[debug.getinfo(self)]=self
  6110. end
  6111. end
  6112.  
  6113. --------------------------------------------------------------------------------
  6114. -- Check whether the 'state' field is consistent with the pipe's content
  6115. -- and maxlength, produce a signal and change it if needed.
  6116. -- Helper for methods which modify content or maxlength.
  6117. --------------------------------------------------------------------------------
  6118. local function updatestate (self)
  6119. local length = self.sndidx - self.rcvidx
  6120. local oldstate = self.state
  6121. local newstate = length==0 and 'empty' or self.maxlength==length and 'full' or 'ready'
  6122. if oldstate ~= newstate then
  6123. self.state=newstate
  6124. sched.signal(self, 'state', newstate)
  6125. end
  6126. end
  6127.  
  6128. --------------------------------------------------------------------------------
  6129. -- Move all entries to the beginning of content, if the following conditions
  6130. -- are both verified:
  6131. -- * there are more than self.wasteabs cells to collect;
  6132. -- * the number of wasted cells is at more than self.wasteprop times bigger
  6133. -- than the number of actually used cells.
  6134. --------------------------------------------------------------------------------
  6135. local function recompact (self)
  6136. local rcvidx = self.rcvidx
  6137. local wasted = rcvidx-1
  6138. if wasted <= self.wasteabs then return end
  6139. local sndidx = self.sndidx
  6140. local used = sndidx - rcvidx
  6141. if wasted <= self.wasteprop * used then return end
  6142. -- Tests passed, proceed to recompaction
  6143. local content
  6144. if sndidx<2000 then content = { select (rcvidx, unpack (self.content)) } else
  6145. -- avoid stack overflows: stacks can't grow beyond LUAI_MAXCSTACK,
  6146. -- which is set to 8000 by default. Our choice of 2000 is just as
  6147. -- arbitrary as 8000.
  6148. local oldcontent
  6149. oldcontent, content = self.content, { }
  6150. for i=1,used do content[i]=oldcontent[i+wasted] end
  6151. end
  6152. self.content, self.rcvidx, self.sndidx = content, 1, used+1
  6153. end
  6154.  
  6155. --------------------------------------------------------------------------------
  6156. -- Read a value, yield the current task if necessary
  6157. --------------------------------------------------------------------------------
  6158. function P :receive (timeout)
  6159. check('?number',timeout)
  6160. local due_date
  6161. while true do
  6162. if self.rcvidx==self.sndidx then
  6163. log('pipe', 'DEBUG', "(%s) empty,(%s) :receive() waits for data", self,sched.running)
  6164. due_date = due_date or timeout and os_time() + timeout
  6165. local timeout = due_date and due_date - os_time()
  6166. if timeout and timeout<=0 or sched.wait(self,'state',timeout)==sched.timer then
  6167. return nil, 'timeout'
  6168. end
  6169. else
  6170. local content, rcvidx = self.content, self.rcvidx
  6171. local x = content[rcvidx]
  6172. content[rcvidx] = false
  6173. self.rcvidx = rcvidx+1
  6174. recompact (self)
  6175. updatestate (self)
  6176. if x~=nil then return x end
  6177. end
  6178. end
  6179. end
  6180.  
  6181. --------------------------------------------------------------------------------
  6182. -- Push a value to read
  6183. --------------------------------------------------------------------------------
  6184. function P :send (x, timeout)
  6185. check ('!nil,?number',x,timeout)
  6186. local maxlength = self.maxlength
  6187. local due_date
  6188. while self.state == 'full' do
  6189. log('pipe', 'DEBUG', "Pipe %s full, :send() blocks until some data is pulled from pipe", self)
  6190. due_date = due_date or timeout and os_time() + timeout
  6191. local timeout = due_date and due_date - os_time()
  6192. if timeout and timeout<=0 or sched.wait(self,'state', timeout)=='timer' then
  6193. log('pipe', 'DEBUG', "Pipe %s :send() timeout", self)
  6194. return nil, 'timeout'
  6195. else
  6196. log('pipe', 'DEBUG', "Pipe %s state changed, retrying to :send()", self)
  6197. end
  6198. end
  6199. local sndidx = self.sndidx
  6200. self.content[sndidx] = x
  6201. self.sndidx = sndidx+1
  6202. updatestate(self)
  6203. return self
  6204. end
  6205.  
  6206. --------------------------------------------------------------------------------
  6207. -- Pushback the last read value
  6208. --------------------------------------------------------------------------------
  6209. function P :pushback (x)
  6210. assert (x~=nil, "Don't :pushback(nil) in a pipe")
  6211. if self.state=='full' then
  6212. return nil, 'length would exceed maxlength'
  6213. end
  6214. local rcvidx = self.rcvidx-1
  6215. if rcvidx==0 then
  6216. table.insert(self.content, 1, x)
  6217. self.sndidx = self.sndidx+1
  6218. else
  6219. self.content[rcvidx] = x
  6220. self.rcvidx = rcvidx
  6221. end
  6222. updatestate (self)
  6223. return self
  6224. end
  6225.  
  6226. --------------------------------------------------------------------------------
  6227. -- Empty the pipe, return what it contained in a list
  6228. --------------------------------------------------------------------------------
  6229. function P :reset()
  6230. local r = self.content
  6231. self.content = { }
  6232. updatestate(self)
  6233. return r
  6234. end
  6235.  
  6236. --------------------------------------------------------------------------------
  6237. -- Try to read without yielding nor removing from internal queue
  6238. --------------------------------------------------------------------------------
  6239. function P :peek()
  6240. return self.content[self.rcvidx]
  6241. end
  6242.  
  6243. --------------------------------------------------------------------------------
  6244. -- Return the number of elements currently in the pipe.
  6245. --------------------------------------------------------------------------------
  6246. function P :length()
  6247. return self.sndidx - self.rcvidx
  6248. end
  6249.  
  6250. --------------------------------------------------------------------------------
  6251. -- Change or remove (by passing nil) the curent pipe length limitation.
  6252. --------------------------------------------------------------------------------
  6253. function P :setmaxlength(maxlength)
  6254. check ('?number',maxlength)
  6255. if self :length() > maxlength then return nil, 'length exceeds new maxlength' end
  6256. if maxlength and maxlength<1 then return nil, 'invalid maxlength' end
  6257. self.maxlength = maxlength
  6258. updatestate (self)
  6259. return self
  6260. end
  6261.  
  6262. --------------------------------------------------------------------------------
  6263. -- Change waste parameters, hence the memory/speed performance compromize.
  6264. --------------------------------------------------------------------------------
  6265. function P :setwaste(abs, prop)
  6266. self.wasteabs, self.wasteprop = abs, prop
  6267. return self
  6268. end
  6269. end
  6270. sched.SigPipe=class('SigPipe',sched.Pipe,sched.Obj)
  6271. do
  6272. local S=sched.SigPipe
  6273. function S:handle(...)
  6274. self:send{...}
  6275. end
  6276. end
  6277.  
  6278. sched.pump=function(pin,f,pout)
  6279. check('?Pipe,?function,?Pipe',pin,f,pout)
  6280. if not (pin or f or pout) then error'No pipes or function' end
  6281. repeat
  6282. local r=pin and pin:receive()
  6283. r=f==nil and r or f(r)
  6284. if r~=nil and pout then pout:send(r) end
  6285. until false
  6286. end
  6287.  
  6288. sched.to=function(pin,pouts)
  6289. repeat
  6290. local x=pin:receive()
  6291. if x~=nil then
  6292. for i,pout in ipairs(pouts) do
  6293. pout:send(x)
  6294. end
  6295. end
  6296. until false
  6297. end
  6298.  
  6299. sched.from=function(pins,pout)
  6300. sched.running:reset()
  6301. for _,pin in ipairs(pins) do
  6302. sched.running:link(pin,'*')
  6303. end
  6304. repeat
  6305. local redo=true
  6306. while redo do
  6307. redo=false
  6308. for _,pin in ipairs(pins) do
  6309. if pin:peek()~=nil then
  6310. pout:send(pin:receive())
  6311. redo=true
  6312. end
  6313. end
  6314. end
  6315. sched.wait(false)
  6316. until false
  6317. end
  6318.  
  6319. ]===================],"CoolisOS/bin/kernel/sched/platform.lua",[===================[local log=require'kernel.log'
  6320. 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
  6321. local coroutine=coroutine
  6322. local tostring,math=tostring,math
  6323. local debug,util=debug,util
  6324. local plat={}
  6325.  
  6326. local sched=sched
  6327. local fil
  6328. env=getfenv()
  6329. setmetatable(env,nil)
  6330.  
  6331. ---Whether or not the coroutine running will yield to the scheduler
  6332. plat.is_running=function()
  6333. return sched and sched.Task.running and sched.Task.running.co==coroutine.running()
  6334. end
  6335.  
  6336. plat.time = os.clock
  6337. local time=plat.time
  6338. local wake_ev=tostring({})
  6339. local Task=sched.Task
  6340.  
  6341. local cc,CC,load=0,1,0
  6342. last_yield,last_return=-math.huge,-math.huge
  6343. plat.info=function()
  6344. 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))
  6345. end
  6346.  
  6347. local yield=function(...)
  6348. log('platform','DETAIL','yielding to platform')
  6349. last_yield=time()
  6350. cc=last_yield-last_return
  6351. local x={coroutine.yield(...)}
  6352. last_return=time()
  6353. CC=last_return-last_yield
  6354. load=cc/(CC+cc)
  6355. log('platform','DETAIL',plat.info())
  6356. return x
  6357. end
  6358.  
  6359. function plat.step(timeout)
  6360. if sched.ready then timeout=0 end
  6361. if timeout then
  6362. local ev,id
  6363. if timeout==0 then
  6364. 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;
  6365.  
  6366. ev=wake_ev
  6367. os.queueEvent(ev)
  6368. else
  6369. return
  6370. end
  6371. else
  6372. ev,id='timer',os.startTimer(timeout)
  6373. end
  6374. local x
  6375. repeat
  6376. x=yield()
  6377. if (ev==nil or x[1]==ev) and (id==nil or id==x[2]) then
  6378. break
  6379. else
  6380. sched.signal(plat,unpack(x))
  6381. if sched.ready then break end
  6382. end
  6383. until false
  6384. return
  6385. else
  6386. if not fil[plat] then
  6387. log('platform','ERROR','no timers or platform listeners, so exiting.')
  6388. sched.stop()
  6389. return
  6390. end
  6391. local filter
  6392. if not next(fil[plat],next(fil[plat])) then
  6393. filter=next(fil[plat])
  6394. end
  6395. sched.signal(plat,unpack(yield(filter)))
  6396. return
  6397. end
  6398. end
  6399.  
  6400. function plat._reset()
  6401. fil=sched.fil
  6402. end
  6403. debug.name(plat,'platform')
  6404. sched.platform=plat
  6405. return plat]===================],"CoolisOS/bin/kernel/sched/timer.lua",[===================[------------------------------------------------------------------------------
  6406. -- timer module
  6407. -- supports one time timer timer.new(positive number)
  6408. -- supports periodic timer timer.new(negative number)
  6409. -- cron-compatible syntax (string conforming to cron syntax)
  6410. -- support simple timers
  6411. ------------------------------------------------------------------------------
  6412. include'kernel.class'
  6413. local linked=require'utils.linked'
  6414. local check=require'kernel.checker'.check
  6415. local shcopy=require'kernel.util'.shcopy
  6416. local error=error
  6417. local read=read
  6418. PACKAGE_NAME='sched'
  6419.  
  6420.  
  6421. local sched=sched
  6422. local fil
  6423. local Obj=sched.Obj
  6424. local os = os
  6425. local math = math
  6426. local tonumber = tonumber
  6427. local assert = assert
  6428. local table = table
  6429. local pairs = pairs
  6430. local next = next
  6431. local type = type
  6432. local _G=_G
  6433. local os_time=os.clock
  6434. local setmetatable=setmetatable
  6435. local rawset=rawset
  6436. local unpack=unpack
  6437. local debug=debug
  6438.  
  6439. env=getfenv()
  6440. setmetatable(env,nil)
  6441.  
  6442. local timer={}
  6443.  
  6444. timer.norm=function(t) --the CC clock moves in steps of 0.05
  6445. t=t-t%0.05
  6446. -- if t<=0 then
  6447. -- error('time values must be non-negative and multiples of 0.05',3)--to be used internally inside timer functions
  6448. -- end
  6449. return t
  6450. end
  6451. local norm=timer.norm
  6452. -------------------------------------------------------------------------------------
  6453. -- Common scheduling code
  6454. -------------------------------------------------------------------------------------
  6455.  
  6456. -------------------------------------------------------------------------------------
  6457. -- These functions must be attached as method :nextevent() to timer objects,
  6458. -- and return the timer's next due date.
  6459. -------------------------------------------------------------------------------------
  6460. local function stimer_nextevent (timer) return nil end
  6461.  
  6462. local events
  6463. local link,link_r
  6464. local n
  6465. -------------------------------------------------------------------------------------
  6466. -- Take a timer, reference it
  6467. -------------------------------------------------------------------------------------
  6468.  
  6469. timer.add = function(t)
  6470. if not link_r[t] then
  6471. local ind
  6472. for val in linked.next_r,link,val do
  6473. if val>t then
  6474. ind=val
  6475. break
  6476. end
  6477. end
  6478. -- print(ind)
  6479. if ind==nil then sched.ready=true end--if the next due date changed, signal the platform to break it's step
  6480. link:insert_r(t,ind)
  6481. end
  6482. end
  6483.  
  6484. -------------------------------------------------------------------------------------
  6485. -- Take a timer, dereference it
  6486. -------------------------------------------------------------------------------------
  6487. timer.remove = function(t)
  6488. link:remove(t)
  6489. end
  6490.  
  6491. -------------------------------------------------------------------------------------
  6492. -- Signal all elapsed timer events.
  6493. -- This must be called by the scheduler every time a due date elapses.
  6494. -------------------------------------------------------------------------------------
  6495. function timer.step()
  6496. if link_r[0]==-1 then return end -- if no timer is set just return and prevent further processing
  6497. local now = os_time()
  6498. while link_r[0]~=-1 and now >= link_r[0] do --about the above redundancy, maybe while loops are heavy?
  6499. local nd = link:remove()
  6500. sched.signal(timer,nd)
  6501. end
  6502. end
  6503.  
  6504. timer.meta={__index=timer}
  6505. setmetatable(timer,{__index=Obj,__tostring=function() return 'Class timer' end})
  6506. -------------------------------------------------------------------------------------
  6507. -- returns the next expiration date
  6508. -------------------------------------------------------------------------------------
  6509. function timer.nextevent()
  6510. -- print(link)
  6511. if link_r[0]~=-1 then return link_r[0] end
  6512. end
  6513.  
  6514. local meta={
  6515. __newindex=function(t,k,v)
  6516. if type(k)~='number' then
  6517. error(tostring(k)..'timer events must be numbers',2)
  6518. end
  6519. -- 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
  6520. -- if k<=0 then
  6521. -- error(k..'time values must be positive',2)
  6522. -- end
  6523. if v==nil then
  6524. timer.remove(k)
  6525. else--could move it here...
  6526. timer.add(k)
  6527. end
  6528. rawset(t,k,v)
  6529. end,
  6530.  
  6531. }
  6532. ---resets the timer module
  6533. timer._reset=function()
  6534. local holder=sched.task(function() end)
  6535. debug.name(holder,'placeholder')
  6536. events=setmetatable({[{}]={[holder]=true}},meta)
  6537. sched.fil[timer]=events
  6538. link=linked()
  6539. link_r=link.r
  6540. timer.link=link
  6541. end
  6542.  
  6543. class.CyclicTimer(sched.Obj)
  6544. do
  6545. local C=CyclicTimer
  6546. local O=sched.Obj
  6547. function C:__init(delta,...)
  6548. O:__init(self)
  6549. check('number,function',delta,f)
  6550. self.delta=norm(delta)
  6551. self.f=f
  6552. self.args={...}
  6553. end
  6554. function C:handle(_,ev)
  6555. ev=ev+self.delta
  6556. self:link{timer={ev}}
  6557. self.f(unpack(self.args))
  6558. end
  6559. end
  6560. timer.cyclic=CyclicTimer
  6561. debug.name(timer,'timer')
  6562. sched.timer=timer
  6563. return timer]===================],"CoolisOS/bin/programs",true,"CoolisOS/bin/programs/bt.lua",[===================[print(1)]===================],"CoolisOS/bin/programs/help.lua",[===================[--By CoolisTheName007
  6564. --CoolisOS/dev/help.lua
  6565.  
  6566. local mon=peripheral.wrap'back'
  6567. mon.setTextScale(1)
  6568. local delay=1
  6569. local textPath='CoolisOS/dev/text'
  6570.  
  6571. local function getText()
  6572. local f=fs.open(textPath,'r')
  6573. local text=f.readAll()
  6574. f.close()
  6575. return text
  6576. end
  6577.  
  6578. local function parse_text(text)
  6579. for line in text:gmatch("([^\n]*)\n?") do
  6580. local t,b=line:match'^#([^#- ]+)-?([^#- ]*)#'
  6581. end
  6582. end
  6583.  
  6584. local function write( sText)
  6585. local term=mon
  6586. local w,h = term.getSize()
  6587. local x,y = term.getCursorPos()
  6588.  
  6589. local nLinesPrinted = 0
  6590. local function newLine()
  6591. if y + 1 <= h then
  6592. term.setCursorPos(1, y + 1)
  6593. else
  6594. term.setCursorPos(1, h)
  6595. term.scroll(1)
  6596. end
  6597. x, y = term.getCursorPos()
  6598. nLinesPrinted = nLinesPrinted + 1
  6599. end
  6600.  
  6601. -- Print the line with proper word wrapping
  6602. while string.len(sText) > 0 do
  6603. local whitespace = string.match( sText, "^[ \t]+" )
  6604. if whitespace then
  6605. -- Print whitespace
  6606. term.write( whitespace )
  6607. x,y = term.getCursorPos()
  6608. sText = string.sub( sText, string.len(whitespace) + 1 )
  6609. end
  6610.  
  6611. local newline = string.match( sText, "^\n" )
  6612. if newline then
  6613. -- Print newlines
  6614. newLine()
  6615. sText = string.sub( sText, 2 )
  6616. end
  6617.  
  6618. local text = string.match( sText, "^[^ \t\n]+" )
  6619. if text then
  6620. sText = string.sub( sText, string.len(text) + 1 )
  6621. if string.len(text) > w then
  6622. -- Print a multiline word
  6623. while string.len( text ) > 0 do
  6624. if x > w then
  6625. newLine()
  6626. end
  6627. term.write( text )
  6628. text = string.sub( text, (w-x) + 2 )
  6629. x,y = term.getCursorPos()
  6630. end
  6631. else
  6632. -- Print a word normally
  6633. if x + string.len(text) - 1 > w then
  6634. newLine()
  6635. end
  6636. term.write( text )
  6637. x,y = term.getCursorPos()
  6638. end
  6639. end
  6640. end
  6641.  
  6642. return nLinesPrinted
  6643. end
  6644.  
  6645. local function wrap(text, limit) --from Kingdaroo
  6646. local lines = {''}
  6647. for word, space in text:gmatch('(%S+)(%s*)') do
  6648. local temp = lines[#lines] .. word .. space:gsub('\n','')
  6649. if #temp > limit then
  6650. table.insert(lines, '')
  6651. end
  6652. if space:find('\n') then
  6653. lines[#lines] = lines[#lines] .. word
  6654.  
  6655. space = space:gsub('\n', function()
  6656. table.insert(lines, '')
  6657. return ''
  6658. end)
  6659. else
  6660. lines[#lines] = lines[#lines] .. word .. space
  6661. end
  6662. end
  6663. return lines
  6664. end
  6665.  
  6666. local function extract(t,s,l)
  6667. local c={}
  6668. for k=s,s+l-1 do
  6669. c[k-s+1]=t[k%(#t)+1]
  6670. end
  6671. return c
  6672. end
  6673.  
  6674. local function print_screen(t)
  6675. mon.clear()
  6676. mon.setCursorPos(1,1)
  6677. local x,y=mon.getSize()
  6678.  
  6679.  
  6680. for i,line in ipairs(t) do
  6681. if (line:sub(1,1)=='#') then
  6682. local tc=colors[line:match'^#([^-#]+)']
  6683. mon.setTextColor(tc or colors.white)
  6684. local bc=colors[line:match'^#[^-#]+%-([^#]+)#']
  6685. mon.setBackgroundColor(bc or colors.black)
  6686. local l=line:match'^#[^#]+#'
  6687. line=line:sub(l:len()+1)
  6688. end
  6689. c=' '
  6690. if (line:sub(-2,-2)=='&') then
  6691. c=line:sub(-1,-1)
  6692. line=line:sub(1,-3)
  6693. end
  6694. line=line..string.rep(c,math.max(x-#line,0))
  6695. write(line..(i~=#t and '\n' or ''))
  6696. end
  6697. end
  6698.  
  6699. local function main()
  6700. local x,y=mon.getSize()
  6701. local text
  6702. local c=-1
  6703. repeat
  6704.  
  6705. local ok,r=pcall(getText)
  6706. if ok then text=r else error(r) end
  6707. local tText=wrap(text,x)
  6708. c=(c+1)%(#tText)
  6709. mon.setTextColor(colors.white)
  6710. mon.setBackgroundColor(colors.black)
  6711. local t=tText
  6712. for i=1,(c+1) do
  6713. local line=t[i]
  6714. if (line:sub(1,1)=='#') then
  6715. local tc=colors[line:match'^#([^-#]+)']
  6716. mon.setTextColor(tc or colors.white)
  6717. local bc=colors[line:match'^#[^-#]+%-([^#]+)#']
  6718. mon.setBackgroundColor(bc or colors.black)
  6719. end
  6720. end
  6721. print_screen(extract(tText,c,y))
  6722.  
  6723. sleep(delay)
  6724. until false
  6725. end
  6726. parallel.waitForAny(main,function() shell.run'shell' end)
  6727. ]===================],"CoolisOS/bin/programs/master.lua",[===================[rednet.open'right'
  6728.  
  6729. function slave()
  6730. local t={
  6731. 1=true
  6732. }
  6733. local send=function(id,n) turtle.select(id) turtle.drop(n) end
  6734. while true do
  6735. local _id,msg=rednet.receive()
  6736. local id,n=msg:match'([^:]):([^:])'
  6737. if id and n then
  6738. n=tonumber(n)
  6739. send(id,n)
  6740. end
  6741. end
  6742. end
  6743.  
  6744. function master()
  6745.  
  6746. delay=5
  6747. function _request(id,n)
  6748. repeat
  6749. rednet.broadcast(id..':'..n)
  6750. t=0
  6751. repeat
  6752. if turtle.getItemCount(1)==n then break end
  6753. sleep(dt)
  6754. t=t+dt
  6755. until t>delay
  6756. until turtle.getItemCount(1)==n
  6757. turtle.drop()
  6758. end
  6759.  
  6760.  
  6761. function request(id,n)
  6762. local r=n%64
  6763. local q=(n-r)/64
  6764. for i=1,q do _request(id,64) end
  6765. _request(id,r)
  6766. end
  6767.  
  6768. end]===================],"CoolisOS/bin/programs/quarry.lua",[===================[--CoolisOS/dev/quarry.lua
  6769. include'kernel.class'
  6770.  
  6771. class.Machine()
  6772. local function link(t)
  6773. if t[1] then t[1].start=true end
  6774. for i,v in ipairs(t) do
  6775. v.next=t[i+1]
  6776. v.index=i
  6777. end
  6778. end
  6779. function Machine:__init(t)
  6780. link(t)
  6781. self.states=t
  6782. end
  6783. function Machine:find()
  6784. for i,v in ipairs(self.states) do
  6785. if v.is() then
  6786. return v
  6787. end
  6788. end
  6789. error'no match for state'
  6790. end
  6791. function Machine:run(state)
  6792. state=state or self.states[1]
  6793. while state do
  6794. state.action()
  6795. state=state.next
  6796. end
  6797. end
  6798.  
  6799. Vec3=require'utils.Vec3'
  6800.  
  6801. local delay=5
  6802. local max_count=300
  6803. Var=require'kernel.class.Var'
  6804. var=Var('ect/quarry.var')
  6805.  
  6806. winding=require'programs.winding'
  6807.  
  6808. include'utils.turtle'
  6809. select=function(t)
  6810. if type(t)~='number' then t=t.slot end
  6811. return turtle.select(t)
  6812. end
  6813. drop=turtle.drop
  6814. refuel=turtle.refuel
  6815. function clear_inv()
  6816. for i=7,16 do
  6817. select(i)
  6818. drop()
  6819. end
  6820. end
  6821. function dirClear(dir) select(16) clear(dir) clear_inv() end
  6822.  
  6823.  
  6824. api.ensureFuel=function()
  6825. if turtle.getFuelLevel()<10 then
  6826. refuel_machine:run()
  6827. end
  6828. end
  6829.  
  6830. F,R,C,M,T,I={slot=1},{slot=2},{slot=3},{slot=4},{slot=5},{slot=6}
  6831. function isInv(a,b,c,d)
  6832. local function is(s)
  6833. local c=turtle.getItemCount(s)
  6834. if c==0 or c==1 then return c
  6835. else error('inv not match') end
  6836. end
  6837. return is(2)==a and is(3)==b and is(4)==c and is(5)==d
  6838. end
  6839. function has(t)
  6840. local c=turtle.getItemCount(t.slot)
  6841. if c~=1 and c~=0 then error'no inv match'
  6842. else return c==1 end
  6843. end
  6844.  
  6845. refuel_states={
  6846. {
  6847. is=function()
  6848. return has(F)
  6849. end,
  6850. action=function()
  6851. dirClear(up)
  6852. select(F)
  6853. place(up)
  6854. end,
  6855. },
  6856. {
  6857. is=function() return not has(F) end,
  6858. action=function()
  6859. select(16)
  6860. drop()
  6861. repeat
  6862. up.suck()
  6863. refuel()
  6864. if turtle.getFuelLevel()>10 then
  6865. break
  6866. else
  6867. drop()
  6868. sleep(1)
  6869. end
  6870. until false
  6871. select(F)
  6872. up.dig()
  6873. end,
  6874. },
  6875. }
  6876.  
  6877. well_states={
  6878. {is=function()
  6879. return isInv(1,1,1,1)
  6880. end,
  6881. action=function()
  6882. select(16)
  6883. down.dig()
  6884. drop()
  6885. force'forward'
  6886. drop()
  6887. down.dig()
  6888. drop()
  6889. select(M.slot)
  6890. place'down'
  6891. select(16)
  6892. local ok,n= force'back'
  6893. if n then clear_inv() end
  6894. select(C.slot)
  6895. place'down'
  6896. end,
  6897. },
  6898. {is=function()
  6899. return isInv(1,0,0,1)
  6900. end,
  6901. action=function()
  6902. select(R.slot)
  6903. place(front)
  6904. sleep(delay)
  6905. end,
  6906. },
  6907. {is=function()
  6908. return isInv(0,0,0,1) and front.detect()
  6909. end,
  6910. action=function()
  6911. select(16)
  6912. local ok,n= force'back'
  6913. if n then clear_inv() end
  6914. end},
  6915. {is=function()
  6916. return isInv(0,0,0,1) and not front.detect()
  6917. end,
  6918. action=function()
  6919. select(T.slot)
  6920. place(front)
  6921. end,
  6922. },
  6923. {is=function()
  6924. return isInv(0,0,0,0)
  6925. end,
  6926. action=function()
  6927. local p=peripheral.wrap('front')
  6928. p.shutdown()
  6929. p.turnOn()
  6930. select(R.slot)
  6931. repeat
  6932. front.suck()
  6933. until turtle.getItemCount(R.slot)==1
  6934. p.shutdown()
  6935. end,
  6936. },
  6937. {is=function()
  6938. return isInv(1,0,0,0)
  6939. end,
  6940. action=function()
  6941. select(T.slot)
  6942. front.dig()
  6943. end,
  6944. },
  6945. {is=function()
  6946. return isInv(1,0,0,1) and not down.detect()
  6947. end,
  6948. action=function()
  6949. select(16)
  6950. force'front'
  6951. drop()
  6952. end,
  6953. },
  6954. {is=function()
  6955. return isInv(1,0,0,1)
  6956. end,
  6957. action=function()
  6958. select(C.slot)
  6959. down.dig()
  6960. end,
  6961. },
  6962. {is=function()
  6963. return isInv(1,1,0,1) and not down.detect()
  6964. end,
  6965. action=function()
  6966. select(16)
  6967. force'front'
  6968. drop()
  6969. end,
  6970. },
  6971. {is=function()
  6972. return isInv(1,1,0,1) and down.detect()
  6973. end,
  6974. action=function()
  6975. select(M.slot)
  6976. down.dig()
  6977. end,
  6978. },
  6979. }
  6980.  
  6981. recharge_states={
  6982. {
  6983. is=function()
  6984. return has(I)
  6985. end,
  6986. action=function()
  6987. dirClear(up)
  6988. select(I)
  6989. place(up)
  6990. end,
  6991. },
  6992. {
  6993. is=function() return ((not has(I)) and has(R)) end,
  6994. action=function()
  6995. select(R)
  6996. repeat
  6997. up.drop()
  6998. until (not has(R))
  6999. end,
  7000. },
  7001. {
  7002. is=function() return not (has(I) or has(R)) end,
  7003. action=function()
  7004. sleep(2)
  7005. select(R)
  7006. repeat
  7007. up.suck()
  7008. until (has(R))
  7009. var.count=0
  7010. select(I)
  7011. up.dig()
  7012. end,
  7013. },
  7014. }
  7015.  
  7016. refuel_machine=Machine(refuel_states)
  7017. recharge_machine=Machine(recharge_states)
  7018.  
  7019. well_machine=Machine(well_states)
  7020.  
  7021. wind_machine={
  7022. run=function(self,center)
  7023. function project(v)
  7024. return {v.x,v.z}
  7025. end
  7026. local p=project((get())-center)
  7027. var.count=var.count or 0
  7028. repeat
  7029. if var.count>max_count then
  7030. recharge_machine:run()
  7031. end
  7032.  
  7033. -- print('actual:',p[1],p[2])
  7034. local n=winding{p[1],p[2]}
  7035. -- print('winding:',n[1],n[2])
  7036. -- print('turning:',n[1]-p[1],0,n[2]-p[2])
  7037. turn{n[1]-p[1],0,n[2]-p[2]}
  7038. -- print('pos,dir:',get())
  7039. local x,z=p[1],p[2]
  7040. local d=math.max(math.abs(x),math.abs(z))
  7041. 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
  7042. well_machine:run()
  7043. end
  7044. local nex=center+Vec3{n[1],0,n[2]}
  7045. -- print('to:',nex)
  7046. goto(nex)
  7047. p=n
  7048. until false
  7049. end,
  7050. }
  7051.  
  7052. function resume()
  7053. order={
  7054. refuel_machine,
  7055. recharge_machine,
  7056. well_machine,
  7057. }
  7058. for _,machine in ipairs(order) do
  7059. local s=machine:find()
  7060. if not s.start then
  7061. machine:run(s)
  7062. end
  7063. end
  7064. end
  7065.  
  7066. function main(_delay,_max_count,reset)
  7067. if reset then var.count=0; var.center=nil end
  7068. delay=_delay or delay
  7069. max_count=_max_count or max_count
  7070. resume()
  7071. var.center=var.center or (get())
  7072. wind_machine:run(Vec3(var.center))
  7073. end
  7074. ]===================],"CoolisOS/bin/programs/question.lua",[===================[--CoolisOS/dev/question.lua
  7075.  
  7076. local questions=loadfile'CoolisOS/dev/questions'()
  7077.  
  7078. local set=rs.setOutput
  7079. local setDoor=function(...) return set('back',...) end
  7080. local setFire=function(...) return set('bottom',...) end
  7081. for i,v in pairs(rs.getSides()) do
  7082. set(v,false)
  7083. end
  7084.  
  7085. local right=function()
  7086. reset_term()
  7087. print'Right Answer! Pass throught the door!'
  7088. setDoor(true)
  7089. sleep(2)
  7090. setDoor(false)
  7091. end
  7092. local function pulse(f,delay)
  7093. f(true)
  7094. sleep(delay)
  7095. f(false)
  7096. sleep(delay)
  7097. end
  7098.  
  7099. local wrong=function()
  7100. reset_term()
  7101. print'Wrong answer! Burn in hell!'
  7102. local t=os.clock()
  7103. while (os.clock()-t)<20 do
  7104. pulse(setFire,1)
  7105. end
  7106. end
  7107.  
  7108. function reset_term()
  7109. term.clear()
  7110. term.setCursorPos(1,1)
  7111. end
  7112.  
  7113. function copy(t)
  7114. local c={}
  7115. for i,v in pairs(t) do
  7116. c[i]=v
  7117. end
  7118. return c
  7119. end
  7120.  
  7121. local get_permut
  7122. do
  7123. local permgen
  7124. permgen = function (a, n, fn)
  7125. if n == 0 then
  7126. fn(a)
  7127. else
  7128. for i=1,n do
  7129. -- put i-th element as the last one
  7130. a[n], a[i] = a[i], a[n]
  7131.  
  7132. -- generate all permutations of the other elements
  7133. permgen(a, n - 1, fn)
  7134.  
  7135. -- restore i-th element
  7136. a[n], a[i] = a[i], a[n]
  7137.  
  7138. end
  7139. end
  7140. end
  7141.  
  7142. --- an iterator over all permutations of the elements of a list.
  7143. -- Please note that the same list is returned each time, so do not keep references!
  7144. -- @param a list-like table
  7145. -- @return an iterator which provides the next permutation as a list
  7146. function permute_iter (a)
  7147. local n = #a
  7148. local co = coroutine.create(function () permgen(a, n, coroutine.yield) end)
  7149. return function () -- iterator
  7150. local code, res = coroutine.resume(co)
  7151. return res
  7152. end
  7153. end
  7154.  
  7155. local function factorial(n)
  7156. if n==0 then return 1 end
  7157. return n*factorial(n-1)
  7158. end
  7159.  
  7160. function get_permut(t)
  7161. local n=math.random()*(factorial(#t))
  7162. local iter=permute_iter(t)
  7163. for i=1,n-1 do iter() end
  7164. local perm=iter()
  7165. return perm
  7166. end
  7167. end
  7168.  
  7169. function test()
  7170. for i,question in pairs(questions) do
  7171. reset_term()
  7172. print(question[1])
  7173. if type(question[2])=='string' then
  7174. print'Type answer.'
  7175. if question[2]~=read() then
  7176. return false
  7177. end
  7178. else
  7179. local permut=get_permut(copy(question[2]))
  7180. for i,answer in pairs(permut) do
  7181. print(i..')'..answer)
  7182. end
  7183. print'Press the number key matching the right answer'
  7184. local id,ev,ok
  7185. repeat
  7186. local t={os.pullEvent()}
  7187. ev,id=t[1],t[2]
  7188. ok,id=pcall(tonumber,id)
  7189. until (ev=='char' and type(id)=='number' and 1<=id and id<=#permut)
  7190. if permut[id]~=question[2][1] then
  7191. return false
  7192. end
  7193. end
  7194. end
  7195. return true
  7196. end
  7197.  
  7198. os.pullEvent=os.pullEventRaw
  7199. wrong()
  7200. repeat
  7201. if test() then
  7202. right()
  7203. else
  7204. wrong()
  7205. end
  7206. until false]===================],"CoolisOS/bin/programs/questions",[===================[local questions={
  7207. {
  7208. [[When was LG Founded??]],
  7209. [[2000]],
  7210. },
  7211. {
  7212. [[Who of the following is NOT a member of LG?]],
  7213. {
  7214. 'DarkRaven',
  7215. 'FatherAbel',
  7216. 'Tuglowz',
  7217. 'Sanitty',
  7218. },
  7219. },
  7220. {
  7221. [[What is Legacy Gaming's website address minus the www]],
  7222. [[legacygaming.com]],
  7223. },
  7224. {
  7225. [[How Does LG Staff handle griefing?]],
  7226. {
  7227. 'All of these',
  7228. 'With a Ban',
  7229. 'With Jail',
  7230. 'With Smite',
  7231. 'None of these (except this one)',
  7232. },
  7233. },
  7234. {
  7235. [[What are you supposed to do if you see someone breaking the rules?]],
  7236. {
  7237. [[Message an Admin AND Email [email protected]]],
  7238. [[Message an Admin]],
  7239. [[Call the police]],
  7240. [[Email [email protected]]],
  7241. [[Message an Admin AND Call the police]],
  7242. [[All of these]],
  7243. },
  7244. },
  7245. {
  7246. [[What is the Owner of LG's Minecraft Name?]],
  7247. [[RoseD83]],
  7248. },
  7249. }
  7250. return questions]===================],"CoolisOS/bin/programs/recharge.lua",[===================[--Recharger
  7251. --log.setLevel('INFO','recharge')
  7252. _log=log
  7253. log=function(...) return _log('recharge',...) end
  7254. args={...}
  7255. tmax=args[1] and tonumber(args[1]) or 1800
  7256. local Var=require'kernel.class.Var'
  7257. var=Var('ect/recharger.var')
  7258. local t=require'utils.turtle'
  7259. local pdir=t.moves.forward
  7260. local tdir=t.moves.down
  7261. g1=pdir.detect
  7262. g2=function() return turtle.getItemCount(1)==1 end
  7263. g3=function() return turtle.getItemCount(2)==1 end
  7264. select=turtle.select
  7265. function out()
  7266. log('INFO','outputing cell')
  7267. select(2)
  7268. repeat
  7269. tdir.drop()
  7270. if not(g3() and g2()) then break else sleep(1) end
  7271. until false
  7272. log('INFO','done')
  7273. end
  7274. function place()
  7275. log('INFO','placing cell')
  7276. select(1)
  7277. repeat
  7278. pdir.place()
  7279. if (not g2()) and g1() then break else sleep(1) end
  7280. until g1()
  7281. log('INFO','done')
  7282. end
  7283. function remove()
  7284. log('INFO','removing cell')
  7285. select(2)
  7286. t.clear(pdir)
  7287. log('INFO','done')
  7288. end
  7289. local BREAK
  7290. function main()
  7291. sleep(5)
  7292. repeat
  7293. log('INFO','starting')
  7294. if g1() and g2() then
  7295. remove()
  7296. end
  7297. if g2() and g3() then
  7298. out()
  7299. sleep(5)
  7300. end
  7301. if g2() and not g3() then
  7302. place()
  7303. var.count=0
  7304. end
  7305. if g1() and not (g2() and g3())then
  7306. log('INFO','recharging')
  7307. var.count=var.count or 0
  7308. tc=var.count
  7309. ti=os.clock()
  7310. select(1)
  7311. repeat
  7312. var.count=(os.clock()-ti)+tc
  7313. print('now:'..var.count..';max='..tmax)
  7314. sleep(1)
  7315. tdir.suck()
  7316. if g2() or BREAK then break end
  7317. until var.count>tmax
  7318. log('INFO','done')
  7319. remove()
  7320. end
  7321. if g3() and not(g1() and g2()) then
  7322. log('INFO','waiting for input')
  7323. select(1)
  7324. repeat
  7325. tdir.suck()
  7326. if not g2() then
  7327. sleep(1)
  7328. else
  7329. break
  7330. end
  7331. until false
  7332. log('INFO','done')
  7333. end
  7334. sleep(1)
  7335. log('INFO','done')
  7336. until false
  7337. end]===================],"CoolisOS/bin/programs/test.lua",[===================[--CoolisOS/dev/test.lua
  7338.  
  7339. -- r()
  7340. -- function r()
  7341. -- v=loadreq.reload'utils.Vec3'
  7342. -- t=loadreq.reload'utils.turtle'
  7343. -- end
  7344. -- r()
  7345. -- function g()
  7346. -- shell.run('CoolisOS/dev/quarry.lua')
  7347. -- m=recharge_machine
  7348. -- end
  7349. -- g()
  7350.  
  7351. -- function r()
  7352. -- w=loadreq.reload'dev.winding'
  7353. -- iter=function(_,p) return w(p) end
  7354. -- for p in iter,nil,{0,0} do
  7355. -- print(p[1],',',p[2])
  7356. -- os.pullEvent'key'
  7357. -- end
  7358. -- end
  7359. -- sched.wait(sched.platform,'*')
  7360. function b()
  7361. printError(debug.traceback())
  7362. end
  7363.  
  7364. function a()
  7365. b()
  7366. end
  7367. a()]===================],"CoolisOS/bin/programs/text",[===================[#white-black#&-
  7368. Hello!
  7369. #green-white#Green!
  7370. ]===================],"CoolisOS/bin/programs/transpose.lua",[===================[while true do
  7371. turtle.suckUp()
  7372. while turtle.getItemCount(1)~=0 do
  7373. turtle.drop()
  7374. end
  7375. sleep(1)
  7376. end]===================],"CoolisOS/bin/programs/winding.lua",[===================[local max=math.max
  7377. local abs=math.abs
  7378.  
  7379. function winding(p)
  7380. local x,y=p[1],p[2]
  7381. local ax,ay=abs(x),abs(y)
  7382. local d=max(ax,ay)
  7383.  
  7384. if x==d and ay~=d then
  7385. return {x,y+1}
  7386. elseif y==d and x~=-d then
  7387. return {x-1,y}
  7388. elseif x==-d and y~=-d then
  7389. return {x,y-1}
  7390. elseif y==-d then
  7391. return {x+1,y}
  7392. elseif y==-d and x==d then
  7393. return {x+1,y}
  7394. else print(p[1],p[2]) _error'not a valid state' end
  7395. end
  7396.  
  7397. return winding
  7398.  
  7399. ]===================],"CoolisOS/bin/utils",true,"CoolisOS/bin/utils/git.lua",[===================[local tArgs, gUser, gRepo, gPath, gBranch = {...}, nil, nil, "", "master"
  7400. local usage = [[
  7401. github <user> <repo> [path] [remote path] [branch]
  7402. Remote path defaults to the root of the repo.
  7403. Path defaults to the download folder.
  7404. Branch defaults to master.
  7405. If you want to leave an option empty use a dot.
  7406. Example: github johnsmith hello-world . foo
  7407. Everything inside the directory foo will be
  7408. downloaded to downloads/hello-world/.
  7409. ]]
  7410. local blackList = [[
  7411. @blacklistedfile
  7412. ]]
  7413.  
  7414. local title = "Github Repo Downloader"
  7415. local fileList = {dirs={},files={}}
  7416. local x , y = term.getSize()
  7417.  
  7418. -- GUI
  7419. function printTitle()
  7420. local line = 2
  7421. term.setCursorPos(1,line)
  7422. for i = 2, x, 1 do write("-") end
  7423. term.setCursorPos((x-title:len())/2,line+1)
  7424. print(title)
  7425. for i = 2, x, 1 do write("-") end
  7426. end
  7427.  
  7428. function writeCenter( str )
  7429. term.clear()
  7430. printTitle()
  7431. term.setCursorPos((x-str:len())/2-1,y/2-1)
  7432. for i = -1, str:len(), 1 do write("-") end
  7433. term.setCursorPos((x-str:len())/2-1,y/2)
  7434. print("|"..str.."|")
  7435. term.setCursorPos((x-str:len())/2-1,y/2+1)
  7436. for i = -1, str:len(), 1 do write("-") end
  7437. end
  7438.  
  7439. function printUsage()
  7440. local str = "Press space key to continue"
  7441. term.clear()
  7442. printTitle()
  7443. term.setCursorPos(1,y/2-4)
  7444. print(usage)
  7445. term.setCursorPos((x-str:len())/2,y/2+7)
  7446. print(str)
  7447. while true do
  7448. local event, param1 = os.pullEvent("key")
  7449. if param1 == 57 then
  7450. sleep(0)
  7451. break
  7452. end
  7453. end
  7454. term.clear()
  7455. term.setCursorPos(1,1)
  7456. end
  7457.  
  7458. -- Download File
  7459. function downloadFile( path, url, name )
  7460. writeCenter("Downloading File: "..name)
  7461. dirPath = path:gmatch('([%w%_%.% %-%+%,%;%:%*%#%=%/]+)/'..name..'$')()
  7462. if dirPath ~= nil and not fs.isDir(dirPath) then fs.makeDir(dirPath) end
  7463. local content = http.get(url)
  7464. local file = fs.open(path,"w")
  7465. file.write(content.readAll())
  7466. file.close()
  7467. end
  7468.  
  7469. -- Get Directory Contents
  7470. function getGithubContents( path )
  7471. local pType, pPath, pName, checkPath = {}, {}, {}, {}
  7472. local response = http.get("https://api.github.com/repos/"..gUser.."/"..gRepo.."/contents/"..path.."/?ref="..gBranch)
  7473. if response then
  7474. response = response.readAll()
  7475. if response ~= nil then
  7476. for str in response:gmatch('"type":"(%w+)"') do table.insert(pType, str) end
  7477. for str in response:gmatch('"path":"([^\"]+)"') do table.insert(pPath, str) end
  7478. for str in response:gmatch('"name":"([^\"]+)"') do table.insert(pName, str) end
  7479. end
  7480. else
  7481. writeCenter( "Error: Can't resolve URL" )
  7482. sleep(2)
  7483. term.clear()
  7484. term.setCursorPos(1,1)
  7485. error()
  7486. end
  7487. return pType, pPath, pName
  7488. end
  7489.  
  7490. -- Blacklist Function
  7491. function isBlackListed( path )
  7492. if blackList:gmatch("@"..path)() ~= nil then
  7493. return true
  7494. end
  7495. end
  7496.  
  7497. -- Download Manager
  7498. function downloadManager( path )
  7499. local fType, fPath, fName = getGithubContents( path )
  7500. for i,data in pairs(fType) do
  7501. if data == "file" then
  7502. checkPath = http.get("https://raw.github.com/"..gUser.."/"..gRepo.."/"..gBranch.."/"..fPath[i])
  7503. if checkPath == nil then
  7504.  
  7505. fPath[i] = fPath[i].."/"..fName[i]
  7506. end
  7507. local path = "downloads/"..gRepo.."/"..fPath[i]
  7508. if gPath ~= "" then path = gPath.."/"..gRepo.."/"..fPath[i] end
  7509. if not fileList.files[path] and not isBlackListed(fPath[i]) then
  7510. fileList.files[path] = {"https://raw.github.com/"..gUser.."/"..gRepo.."/"..gBranch.."/"..fPath[i],fName[i]}
  7511. end
  7512. end
  7513. end
  7514. for i, data in pairs(fType) do
  7515. if data == "dir" then
  7516. local path = "downloads/"..gRepo.."/"..fPath[i]
  7517. if gPath ~= "" then path = gPath.."/"..gRepo.."/"..fPath[i] end
  7518. if not fileList.dirs[path] then
  7519. writeCenter("Listing directory: "..fName[i])
  7520. fileList.dirs[path] = {"https://raw.github.com/"..gUser.."/"..gRepo.."/"..gBranch.."/"..fPath[i],fName[i]}
  7521. downloadManager( fPath[i] )
  7522. end
  7523. end
  7524. end
  7525. end
  7526.  
  7527. -- Main Function
  7528. function main( path )
  7529. writeCenter("Connecting to Github")
  7530. downloadManager(path)
  7531. for i, data in pairs(fileList.files) do
  7532. downloadFile( i, data[1], data[2] )
  7533. end
  7534. writeCenter("Download completed")
  7535. sleep(2,5)
  7536. term.clear()
  7537. term.setCursorPos(1,1)
  7538. end
  7539.  
  7540. -- Parse User Input
  7541. function parseInput( user, repo , dldir, path, branch )
  7542. if path == nil then path = "" end
  7543. if branch ~= nil then gBranch = branch end
  7544. if repo == nil then printUsage()
  7545. else
  7546. gUser = user
  7547. gRepo = repo
  7548. if dldir ~= nil then gPath = dldir end
  7549. main( path )
  7550. end
  7551. end
  7552.  
  7553. if not http then
  7554. writeCenter("You need to enable the HTTP API!")
  7555. sleep(3)
  7556. term.clear()
  7557. term.setCursorPos(1,1)
  7558. else
  7559. for i=1, 5, 1 do
  7560. if tArgs[i] == "." then tArgs[i] = nil end
  7561. end
  7562. parseInput( tArgs[1], tArgs[2], tArgs[3], tArgs[4], tArgs[5] )
  7563. end]===================],"CoolisOS/bin/utils/linked.lua",[===================[include'kernel.class'
  7564. local pairs=pairs
  7565. local print=print
  7566.  
  7567. class.Linked()
  7568.  
  7569. function Linked:__init(t)
  7570. self.r,self.l={[0]=-1},{[-1]=0}
  7571. if t then self:append_r(t) end
  7572. end
  7573.  
  7574. function Linked._insert(t,val,i_l,i_r)
  7575. if t.r[val] then return end
  7576. i_l,i_r=i_l or 0,i_r or -1
  7577. --handle left right
  7578. t.r[i_l], t.l[val], t.r[val], t.l[i_r]
  7579. = val, i_l, i_r, val
  7580. end
  7581.  
  7582. function Linked.insert_l(t,val,i_l)
  7583. return Linked._insert(t,val,i_l,t.r[i_l or 0])
  7584. end
  7585.  
  7586. function Linked.insert_r(t,val,i_r)
  7587. return Linked._insert(t,val,t.l[i_r or -1],i_r)
  7588. end
  7589.  
  7590. function Linked.remove(t,val)
  7591. val=val or t.r[0]
  7592. 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
  7593. if val==-1 then val=nil end
  7594. return val,t.r[val]
  7595. end
  7596.  
  7597. function Linked.next_r(t,ind)
  7598. local _=t.r[ind or 0]
  7599. if _~=-1 then return _ end
  7600. end
  7601.  
  7602. function Linked.next_l(t,ind)
  7603. local _=t.r[ind or -1]
  7604. if _~=0 then return _ end
  7605. end
  7606.  
  7607. function Linked.__tostring(t)
  7608. s={}
  7609. for i in self.next_r,t,nil do
  7610. table.insert(s,tostring(i))
  7611. end
  7612. return 'Linked instance:'..table.concat(s,'<>')
  7613. end
  7614.  
  7615. function Linked:append_r(t)
  7616. local old
  7617. for i,v in ipairs(t) do
  7618. self:insert_r(v,old)
  7619. -- print(v,old)
  7620. -- read()
  7621. old=v
  7622. end
  7623. end
  7624.  
  7625. function Linked:has(val)
  7626. return self.r[val]~=nil
  7627. end
  7628. function Linked:isempty()
  7629. return self.r[0]==-1
  7630. end
  7631. Linked.append=Linked.append_r
  7632. Linked.push=Linked.insert_r
  7633. Linked.pop=Linked.remove
  7634.  
  7635. function Linked.test()
  7636. t={'a',3,4}
  7637. l=Linked(t)
  7638. print(l)
  7639. l:remove(3)
  7640. print(l)
  7641. print(l:has(4),l:has(3))
  7642. return l
  7643. end
  7644.  
  7645.  
  7646.  
  7647. return Linked]===================],"CoolisOS/bin/utils/luau.lua",[===================[-- luau: Lua-upgraded
  7648.  
  7649. -- Since we're sharing our scope with the user, make a table to keep
  7650. -- all our stuff in, so we pollute the namespace as little as possible:
  7651. luau_={}
  7652. luau_.n = 0 -- current line number
  7653. luau_.opt = {} -- options data
  7654. luau_.mac = {} -- macros
  7655. -- A default macro:
  7656. luau_.mac.la = {['def']='os.loadAPI("%1")',['pnum']=1}
  7657.  
  7658. -- Default option settings:
  7659. luau_.opt.skip_lines={
  7660. ['type'] = 'number',
  7661. ['value'] = 0,
  7662. ['default'] = 0,
  7663. ['desc']='Number of blank lines to skip between input/output pairs.'
  7664. }
  7665. luau_.opt.use_color={
  7666. ['type'] = 'boolean',
  7667. ['value'] = true,
  7668. ['default'] = true,
  7669. ['desc']='Whether to use color, if available.'
  7670. }
  7671.  
  7672. -- Some variables we do want to expose:
  7673. in_ = {} -- input history
  7674. out_ = {} -- output history
  7675.  
  7676.  
  7677. -- Help topics:
  7678. luau_.help = {}
  7679.  
  7680. luau_.help.summary = 'The following topics are available, and can be abbreviated--for instance, "/help sub" for "/help substitution".'
  7681.  
  7682. 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.'
  7683.  
  7684. luau_.help.history = 'The tables in_ and out_ hold the input and output histories, and can be referenced, for example: "result=out_[9]".'
  7685.  
  7686. 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.'
  7687.  
  7688. 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.'
  7689.  
  7690. 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".'
  7691.  
  7692. 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.'
  7693.  
  7694. -- Build a sorted list of help topics:
  7695. luau_.helpkeys = {}
  7696. for k in pairs(luau_.help) do
  7697. if k~='summary' then
  7698. table.insert(luau_.helpkeys, k)
  7699. end
  7700. end
  7701. table.sort(luau_.helpkeys)
  7702.  
  7703. -- Define the appropriate color function:
  7704. if term.isColor() then
  7705. term.setTextColor(colors.gray)
  7706. luau_.setColor = function(c)
  7707. if luau_.opt.use_color.value then
  7708. term.setTextColor(c)
  7709. else
  7710. term.setTextColor(gray)
  7711. end
  7712. end
  7713. else
  7714. luau_.setColor = function(c) return end
  7715. end
  7716.  
  7717. luau_.terminated = false
  7718. -- End of initialization code.
  7719.  
  7720. function luau_.terminate()
  7721. -- Goodbye, cruel world:
  7722. luau_.terminated = true
  7723. end
  7724.  
  7725. function luau_.separator()
  7726. -- A line of dashes, of the appropriate length:
  7727. return(string.rep('-', term.getSize()))
  7728. end
  7729.  
  7730. function luau_.loadData()
  7731. -- Load config options and macros:
  7732. local h
  7733. if fs.exists('luau.conf') then
  7734. h = fs.open('luau.conf','r')
  7735. local opt, mac
  7736. opt = textutils.unserialize(h.readLine())
  7737. mac = textutils.unserialize(h.readLine())
  7738. h.close()
  7739. if type(opt)=='table' and type(mac)=='table' then
  7740. luau_.opt = opt
  7741. luau_.mac = mac
  7742. return true
  7743. else
  7744. luau_.showError('Unable to load configuration; /luau.conf is corrupt. Exiting. (Delete or rename /luau.conf to start fresh)')
  7745. return false
  7746. end
  7747. else
  7748. luau_.setColor(colors.blue)
  7749. print('Configuration file /luau.conf does not exist; creating with default values.')
  7750. luau_.saveData()
  7751. return true
  7752. end
  7753. end
  7754.  
  7755. function luau_.saveData()
  7756. -- Save config options and macros:
  7757. h = fs.open('luau.conf','w')
  7758. h.writeLine(textutils.serialize(luau_.opt))
  7759. h.writeLine(textutils.serialize(luau_.mac))
  7760. h.close()
  7761. end
  7762.  
  7763. function luau_.firstMatch(s, t)
  7764. for i, k in ipairs(t) do
  7765. if string.find(k, s) == 1 then
  7766. return k
  7767. end
  7768. end
  7769. return nil
  7770. end
  7771.  
  7772. function luau_.showError(errorText)
  7773. -- Report errors to the user
  7774. luau_.setColor(colors.red)
  7775. print(errorText)
  7776. luau_.setColor(colors.gray)
  7777. end
  7778.  
  7779. function luau_.trim(s)
  7780. -- Trim leading/trailing spaces from string s:
  7781. return (s:gsub("^%s*(.-)%s*$", "%1"))
  7782. end
  7783.  
  7784. function luau_.macro(s)
  7785. -- !<n> to re-evaluate n-th input:
  7786. local a, b, n
  7787. a, b, n = s:find('!(%d+)')
  7788. n = tonumber(n)
  7789. if a == 1 then
  7790. if in_[n] then
  7791. s = in_[n]
  7792. else
  7793. luau_.showError("History entry "..tostring(n).." not found.")
  7794. s = ''
  7795. end
  7796. return s
  7797. end
  7798. -- s/foo/bar/ substitutions
  7799. a, b, find, replace, flags = s:find('s/(.+)/(.*)/(.*)')
  7800. if find and in_[luau_.n] then
  7801. s = in_[luau_.n-1]:gsub(find, replace)
  7802. return s
  7803. end
  7804. -- Macro expansion:
  7805. local lMark, rMark = '>tboss>', '<tboss<'
  7806. -- This lMark/rMark business is an ugly hack that wouldn't be
  7807. -- necessary if I knew how to directly capture the stuff INSIDE a
  7808. -- balanced expression captured with %b[], i.e. without the brackets.
  7809. for name, macro in pairs(luau_.mac) do
  7810. local pattern = '%%'..name..string.rep('(%b[])',macro.pnum)
  7811. -- This is a workaround for a bug in the string library:
  7812. local s_ = s..string.rep('[ ]',macro.pnum)
  7813. local a, b = s_:find(pattern)
  7814. if b and b<=s:len() then
  7815. if s_:find(pattern) then
  7816. local def = macro.def:gsub('(%%%d)',lMark..'%1'..rMark)
  7817. s_ = s_:gsub(pattern, def)
  7818. s_ = s_:gsub(lMark..'%[', '')
  7819. s_ = s_:gsub('%]'..rMark, '')
  7820. s = s_:sub(1, s_:len()-3*macro.pnum)
  7821. end
  7822. end
  7823. end
  7824. return s
  7825. end
  7826.  
  7827. function luau_.columnList(t)
  7828. -- Print the elements of t in columns:
  7829. -- Compute the max length of the names so we can decide
  7830. -- how many columns to print them in:
  7831. local maxLen = 0
  7832. for i,k in ipairs(t) do
  7833. if string.len(k) > maxLen then maxLen = string.len(k) end
  7834. end
  7835. local spacing = 2 -- Spacing between columns
  7836. local w, h = term.getSize()
  7837. local nCols = math.floor((w+spacing)/(maxLen+spacing))
  7838. -- Print all the names in columns:
  7839. for i, k in ipairs(t) do
  7840. if (i%nCols == 0) or (i == #t) then
  7841. print(k)
  7842. else
  7843. write(k..string.rep(' ', maxLen+spacing-string.len(k)))
  7844. end
  7845. end
  7846. end
  7847.  
  7848. function luau_.showHelp(topic)
  7849. luau_.setColor(colors.blue)
  7850. topic = tostring(topic)
  7851. if topic ~= '' then
  7852. -- Do partial matching:
  7853. local mtopic = luau_.firstMatch(topic, luau_.helpkeys)
  7854. if luau_.help[mtopic] then
  7855. local m, n = term.getSize()
  7856. textutils.pagedPrint('('..mtopic..') '..luau_.help[mtopic],n-3)
  7857. else
  7858. print('No help found for "'..topic..'". Type "help" for a list of topics.')
  7859. end
  7860. else
  7861. -- Print help summary and list of topics:
  7862. print(luau_.help.summary)
  7863. print(luau_.separator())
  7864. --luau_.setColor(colors.lightBlue)
  7865. luau_.columnList(luau_.helpkeys)
  7866. end
  7867. end
  7868.  
  7869. function luau_.searchHistory(s)
  7870. -- Print a list of history entries matching s.
  7871. -- Should refactor this into a search and a print.
  7872. local nFound = 0
  7873. for i,v in ipairs(in_) do
  7874. if string.find(v,s) then
  7875. print(i..': '..v)
  7876. nFound = nFound+1
  7877. end
  7878. end
  7879. return nFound
  7880. end
  7881.  
  7882. function luau_.keySort(a, b)
  7883. -- Order function for table keys, returns true if a<b:
  7884. if type(a)=='number' and type(b)=='number' then
  7885. return a<b
  7886. elseif type(a)=='number' then
  7887. return true
  7888. elseif type(b)=='number' then
  7889. return false
  7890. elseif type(a)=='string' and type(b)=='string' then
  7891. return a<b
  7892. elseif type(a)=='string' then
  7893. return true
  7894. elseif type(b)=='string' then
  7895. return false
  7896. else
  7897. return (type(a)<type(b))
  7898. end
  7899. end
  7900.  
  7901. function luau_.serial(v)
  7902. -- Wrapper for textutils.serialize, to work with all types
  7903. if string.find("nil boolean number string table", type(v)) then
  7904. local err, str = pcall(function() textutils.serialize(v) end)
  7905. if err then str=tostring(v) end
  7906. return str
  7907. else
  7908. return tostring(v)
  7909. end
  7910. end
  7911.  
  7912. function luau_.setMacro(p, overwrite)
  7913. -- Define or redefine a macro:
  7914. luau_.setColor(colors.yellow)
  7915. if p=='' then
  7916. -- Display currently defined macros:
  7917. print("The following macros are currently defined:")
  7918. print(luau_.separator())
  7919. for k in pairs(luau_.mac) do
  7920. print("%"..k.."="..luau_.mac[k].def)
  7921. end
  7922. else
  7923. -- Should be in the form "%name=def":
  7924. local a, b, name, def = p:find('%%([%w_]+)=(.*)')
  7925. if name then
  7926. -- See if it's already defined:
  7927. if luau_.mac[name] and not overwrite then
  7928. print('Macro %'..name..' is already defined. Use /Macro to overwrite it.')
  7929. return false
  7930. end
  7931. -- See how many params:
  7932. local n = 0
  7933. def:gsub('%%(%d+)', function(s) n=tonumber(s) end)
  7934. luau_.mac[name] = {['def']=def, ['pnum']=n}
  7935. -- Save it right away. We could wait until we exit, but
  7936. -- during development crashes are likely so let's not
  7937. -- lose people's work.
  7938. luau_.saveData()
  7939. else
  7940. luau_.showError('Invalid macro definition (see /help macro).')
  7941. return false
  7942. end
  7943. end
  7944. return true
  7945. end
  7946.  
  7947. function luau_.setOpt(p)
  7948. luau_.setColor(colors.yellow)
  7949. if p=='' then
  7950. print('The following options are available. Type "/option <option_name>=<value>" to change, or "/option <option_name>" to get a description.')
  7951. local keys = {}
  7952. for k in pairs(luau_.opt) do keys[#keys+1]=k end
  7953. table.sort(keys, luau_.keySort)
  7954. print(luau_.separator())
  7955. for i, k in ipairs(keys) do
  7956. print(k..'='..tostring(luau_.opt[k].value))
  7957. end
  7958. elseif p:find('=') then
  7959. local a, b, opt, val = p:find("([%w_]+)%s*=%s*(.*)")
  7960. if luau_.opt[opt] then
  7961. -- Make sure the types are right:
  7962. local t = luau_.opt[opt].type
  7963. local ok = false
  7964. if t=='boolean' then
  7965. local v = val:lower()
  7966. if v=='true' or v=='1' or v=='yes' or v=='y' then
  7967. luau_.opt[opt].value = true
  7968. ok = true
  7969. elseif v=='false' or v=='0' or v=='no' or v=='n' then
  7970. luau_.opt[opt].value = false
  7971. ok = true
  7972. end
  7973. elseif t=='number' then
  7974. local v = tonumber(val)
  7975. if v then
  7976. luau_.opt[opt].value = v
  7977. ok = true
  7978. end
  7979. elseif t=='string' then
  7980. luau_.opt[opt].value = val
  7981. ok = true
  7982. end
  7983. if ok then
  7984. luau_.saveData()
  7985. else
  7986. print('Value for '..opt..' must be of type '..t..'.')
  7987. end
  7988. else
  7989. print('Option "'..p..'" not found. Type "/option" for a list.')
  7990. end
  7991. else
  7992. if luau_.opt[p] then
  7993. print(luau_.opt[p].desc..' [type='..luau_.opt[p].type..', value='..tostring(luau_.opt[p].value)..', default='..tostring(luau_.opt[p].default)..']')
  7994. else
  7995. print('Option "'..p..'" not found. Type "/option" for a list.')
  7996. end
  7997. end
  7998. end
  7999.  
  8000. function luau_.showCommands(p)
  8001. luau_.setColor(colors.blue)
  8002. if p=='' then
  8003. print('The following commands are available. Type "/command <command-name>" for details.')
  8004. print(luau_.separator())
  8005. luau_.columnList(luau_.cmdNames)
  8006. else
  8007. local cmd = luau_.firstMatch(p, luau_.cmdNames)
  8008. if cmd then
  8009. print('/'..cmd..': '..luau_.cmd[cmd].desc)
  8010. else
  8011. print('Command "'..p..'" not found. Type "/commands" for a list.')
  8012. end
  8013. end
  8014. end
  8015.  
  8016. -- Now that we've defined all our handler functions, we can set up the command list:
  8017. luau_.cmd = {}
  8018. luau_.cmd.help = {
  8019. ['handler'] = luau_.showHelp,
  8020. ['desc'] = 'Online help. Type "/help <topic>", or "/help" for a list of topics.',
  8021. }
  8022. luau_.cmd.exit = {
  8023. ['handler'] = luau_.terminate,
  8024. ['desc'] = 'Exits Luau and returns you to the shell or wherever else you launched it from.',
  8025. }
  8026. luau_.cmd.history = {
  8027. ['handler'] = luau_.searchHistory,
  8028. ['desc'] = 'Typing "/history" will simply print the entire history buffer, while "/history <pattern>" will only show entries matching <pattern>.',
  8029. }
  8030. luau_.cmd.macro = {
  8031. ['handler'] = function(p) luau_.setMacro(p, false) end,
  8032. ['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.',
  8033. }
  8034. luau_.cmd.Macro = {
  8035. ['handler'] = function(p) luau_.setMacro(p, true) end,
  8036. ['desc'] = 'Same as "/macro", but able to overwrite existing definitions.',
  8037. }
  8038. luau_.cmd.options = {
  8039. ['handler'] = luau_.setOpt,
  8040. ['desc'] = 'Type "/options" for a list of all options, "/option <name>" for an option\'s description, or "/option <name>=<value>" to change it.',
  8041. }
  8042. luau_.cmd.commands = {
  8043. ['handler'] = luau_.showCommands,
  8044. ['desc'] = 'Type "/commands" for a list of commands, or "/command <command-name>" to view a description of a particular one.',
  8045. }
  8046. luau_.cmdNames = {}
  8047. for k in pairs(luau_.cmd) do table.insert(luau_.cmdNames, k) end
  8048. table.sort(luau_.cmdNames)
  8049.  
  8050.  
  8051. function luau_.go()
  8052. -- Load config options and macros:
  8053. if not luau_.loadData() then return false end
  8054. luau_.setColor(colors.blue)
  8055. print()
  8056. print('LUAU--LUA Upgraded v0.1 by Tinyboss\nType "/exit" to exit, "/help" for help.')
  8057. while not luau_.terminated do
  8058. luau_.setColor(colors.gray)
  8059. for i=1,luau_.opt.skip_lines.value do print() end
  8060. luau_.n = luau_.n+1
  8061. -- Get the next input, passing the input history for recall by up-arrow:
  8062. local input = ''
  8063. while luau_.trim(input)=='' do
  8064. luau_.setColor(colors.lime)
  8065. write(' in_['..luau_.n..']: ')
  8066. luau_.setColor(colors.lightGray)
  8067. input = luau_.trim(read(false, in_,getfenv(1)))
  8068. end
  8069. in_[luau_.n] = input
  8070. luau_.handled = false
  8071. -- ? is a shortcut for print, with benefits:
  8072. if in_[luau_.n]:sub(1,1) == "?" then
  8073. rest_ = luau_.trim(in_[luau_.n]:sub(2))
  8074. if rest_ == "" then
  8075. -- If they just typed "?", they probably wanted help:
  8076. luau_.setColor(colors.blue)
  8077. print('"?foo" is a synonym for "print(foo)". Use "/help" for help.')
  8078. luau_.handled = true
  8079. elseif type(getfenv(1)[rest_])=="table" then
  8080. -- If it's a table, display it:
  8081. local keys = {}
  8082. for k in pairs(getfenv(1)[rest_]) do
  8083. keys[#keys+1]=k
  8084. end
  8085. table.sort(keys, luau_.keySort)
  8086. local kv = {}
  8087. for i,k in ipairs(keys) do
  8088. kv[i] = luau_.serial(k)..'='
  8089. ..luau_.serial(getfenv(1)[rest_][k])
  8090. end
  8091. print('{',table.concat(kv, ', '),'}')
  8092. luau_.handled = true
  8093. else
  8094. in_[luau_.n] = 'print('..in_[luau_.n]:sub(2)..')'
  8095. end
  8096. end
  8097. if not luau_.handled then
  8098. -- See if it's a slash command:
  8099. local a_, b_, cmd_, rest_ = in_[luau_.n]:find('/(%a+)%s*(.*)')
  8100. if a_==1 then
  8101. cmd_ = luau_.firstMatch(cmd_, luau_.cmdNames)
  8102. if cmd_ then
  8103. luau_.cmd[cmd_].handler(rest_)
  8104. else
  8105. luau_.setColor(colors.yellow)
  8106. print('Invalid command. (Try "/help".)')
  8107. end
  8108. else
  8109. -- Do macro substitution before passing it to Lua:
  8110. in_[luau_.n] = luau_.macro(in_[luau_.n])
  8111. luau_.setColor(colors.gray)
  8112. is_print_ = string.sub(in_[luau_.n], 1, 6)=='print('
  8113. -- Try it as an expression first:
  8114. func_, err_ = loadstring('out_[luau_.n]='..in_[luau_.n])
  8115. if err_ then
  8116. -- Try it as a statement (e.g. "pi=3"):
  8117. func_, err_ = loadstring(in_[luau_.n])
  8118. if err_ then
  8119. luau_.showError('(loadstring)'..err_)
  8120. else
  8121. setfenv(func_, getfenv(1))
  8122. success_, err_ = pcall(func_)
  8123. if not success_ then
  8124. luau_.showError(err_)
  8125. end
  8126. end
  8127. else
  8128. -- It worked as an expression, so store and show the result:
  8129. setfenv(func_, getfenv(1))
  8130. success_, err_ = pcall(func_)
  8131. if success_ then
  8132. if out_[luau_.n] and not is_print_ then
  8133. luau_.setColor(colors.green)
  8134. write('out_['..luau_.n..']: ')
  8135. luau_.setColor(colors.gray)
  8136. print(out_[luau_.n])
  8137. end
  8138. else
  8139. luau_.showError(err_)
  8140. end
  8141. end
  8142. end
  8143. end
  8144. end
  8145. -- Save config and macro data:
  8146. luau_.saveData()
  8147. end
  8148.  
  8149. luau_.go()]===================],"CoolisOS/bin/utils/path.lua",[===================[---
  8150. -- Path utils.
  8151. --
  8152. -- **Definition of path** used by this file:
  8153. --
  8154. -- - it is **not Lua path**, Lua path meaning the way Lua provide table indexing.
  8155. -- - especially, **'table[ ]'** notation is not supported
  8156. -- - **element separator is '.'** (dot)
  8157. -- - provided **path is always cleaned** before being processed:
  8158. -- - if the path starts and/or ends with one or several dots, there are automatically removed.<br />
  8159. -- e.g.: `'...toto.tutu....'` means `'toto.tutu'`
  8160. -- - internal repetitions of successives dots are replaced by a single dot.<br />
  8161. -- e.g.: `'toto...tutu.tata..foo.bar'` means `'toto.tutu.tata.foo.bar'`
  8162. -- - if a path element can be **converted to a number**, then it is returned as a number,
  8163. -- otherwise all elements or paths are returned as strings.<br />
  8164. -- e.g.: `split('a.2', 1)` -> `('a', 2)` (where 2 is returned as a number) <br />
  8165. -- e.g.: `split('a.b.2e3.c', 1)` -> `('a', 'b.2000.c')`
  8166. --
  8167. -- @module utils.path
  8168. --
  8169. local check = require"kernel.checker".check
  8170.  
  8171. local M = { }
  8172. --public API
  8173. local split,clean,segments, get, set, gsplit, concat, find
  8174.  
  8175. local function pathconcat(pt, starti, endi)
  8176. local t = {}
  8177. local prev
  8178. local empties = 0
  8179. starti = starti or 1
  8180. endi = endi or #pt
  8181.  
  8182. for i = starti, endi do
  8183. local v = pt[i]
  8184. if not v then break
  8185. elseif v == '' then
  8186. empties = empties+1
  8187. else
  8188. table.insert(t, prev)
  8189. prev = v
  8190. end
  8191. end
  8192. table.insert(t, prev)
  8193. --log('PATH', 'INFO', "pathconcat(%s, %d, %d) generates table %s, wants indexes %d->%d",
  8194. -- sprint(pt), starti, endi, sprint(t), 1, endi-starti+1-empties)
  8195. return table.concat(t, '.', 1, endi-starti+1-empties)
  8196. end
  8197.  
  8198. --------------------------------------------------------------------------------
  8199. -- Concatenates a sequence of path strings together.
  8200. --
  8201. -- @function [parent=#utils.path] concat
  8202. -- @param varargs list of strings to concatenate into a valid path
  8203. -- @return resulting path as a string
  8204. --
  8205.  
  8206. function concat(...)
  8207. return pathconcat({...})
  8208. end
  8209.  
  8210. --------------------------------------------------------------------------------
  8211. -- Cleans a path.
  8212. --
  8213. -- Removes trailing/preceding/doubling '.'.
  8214. --
  8215. -- @function [parent=#utils.path] clean
  8216. -- @param path string containing the path to clean.
  8217. -- @return cleaned path as a string.
  8218. --
  8219.  
  8220. function clean(path)
  8221. check('string',path)
  8222. local p = segments(path)
  8223. return pathconcat(p)
  8224. end
  8225.  
  8226. --------------------------------------------------------------------------------
  8227. -- Sets a value in a tree-like table structure.
  8228. --
  8229. -- The value to set is indicated by the path relative to the table.
  8230. -- This function creates the table structure to store the value, unless the value to set is nil.
  8231. -- If the value to set is nil and the table structure already exists then the value is set to nil.
  8232. -- If the value is not nil, then the table structure is always created/overwritten and the value set.
  8233. --
  8234. -- @function [parent=#utils.path] set
  8235. -- @param t table where to set the value.
  8236. -- @param path can be either a string (see @{#(utils.path).split})
  8237. -- or an array where path[1] is the root and path[n] is the leaf.
  8238. -- @param value the value to set.
  8239. --
  8240.  
  8241. function set(t, path, value)
  8242. check('table,string|table',t, path)
  8243. local p = type(path)=='string' and segments(path) or path
  8244. local k = table.remove(p)
  8245. local t = find(t, p, value~=nil) -- only create the table structure if the value to set is non nil!
  8246. -- local t = findtable(t, p, true) -- for the creation of the table structure, even if the value to set is non nil!
  8247. if t then t[k] = value end
  8248. end
  8249.  
  8250. --------------------------------------------------------------------------------
  8251. -- Gets the value of a table field. The field can be in a sub table.
  8252. --
  8253. -- The field to get is indicated by a path relative to the table.
  8254. --
  8255. -- @function [parent=#utils.path] get
  8256. -- @param t table where to set the value.
  8257. -- @param path can be eiher a string (see @{split}) or an array where path[1] is the root and path[n] is the leaf.
  8258. -- @return value if the field is found.
  8259. -- @return nil otherthise.
  8260. --
  8261. function get(t, path)
  8262. check('table,string|table',t,path)
  8263. local p = type(path)=='string' and segments(path) or path
  8264. local k = table.remove(p)
  8265. if not k then return t end
  8266. local t = find(t, p)
  8267. return t and t[k]
  8268. end
  8269.  
  8270.  
  8271. --------------------------------------------------------------------------------
  8272. -- Enumerates path partitions in a for-loop generator, starting from the right.
  8273. --
  8274. -- For instance, `gsplit "a.b.c"` will generate successively
  8275. -- `("a.b.c", ""), ("a.b", "c"), ("a", "b.c"), ("", "a.b.c")`.
  8276. --
  8277. -- @function [parent=#utils.path] gsplit
  8278. -- @param path the path as a string
  8279. -- @return the for-loop iterator function
  8280. --
  8281.  
  8282. function gsplit (path)
  8283. check ('string',path)
  8284. local segs = segments(path)
  8285. local nsegs = #segs
  8286. local limit = nsegs
  8287. local function f()
  8288. if limit == -1 then return nil, nil end
  8289. local a, b = pathconcat(segs, 1, limit), pathconcat(segs, limit+1, nsegs)
  8290. limit = limit - 1
  8291. return a, b
  8292. end
  8293. return f
  8294. end
  8295.  
  8296. --------------------------------------------------------------------------------
  8297. -- Splits a path into two halves, can be used to get path root, tail etc.
  8298. --
  8299. -- The content of the two halves depends on 'n' param: there will be `n` segments in the first half if `n>0`,
  8300. -- `-n` segments in the second half if `n<0`.
  8301. --
  8302. -- If there are less then `n` segments, returns the path argument followed by
  8303. -- an empty path.
  8304. --
  8305. -- If there are less then `-n` segments, returns an empty path followed by the
  8306. -- path argument.
  8307. --
  8308. -- Note that if a half is a single element and that this element can be converted into a number,
  8309. -- it is returned as a number.
  8310. --
  8311. -- @function [parent=#utils.path] split
  8312. -- @param path the path as a string
  8313. -- @param n number defining how the path is splitted (see above description).
  8314. -- @return the two halves
  8315. -- @usage local root, tail = split('a.b.c', 1)
  8316. -- ->root contains 'a', tail contains 'b.c'
  8317. --
  8318.  
  8319. function split(path, n)
  8320. check('string,number',path,n)
  8321. local segments = segments(path)
  8322. if n>#segments then return path, ''
  8323. elseif -n>#segments then return '', path
  8324. else
  8325. if n<0 then n=#segments+n end
  8326. return pathconcat(segments, 1, n), pathconcat(segments, n+1, #segments)
  8327. end
  8328. end
  8329.  
  8330. --------------------------------------------------------------------------------
  8331. -- Splits a path into segments.
  8332. --
  8333. -- Each segment is delimited by '.' pattern.
  8334. --
  8335. -- @function [parent=#utils.path] segments
  8336. -- @param path string containing the path to split.
  8337. -- @return list of split path elements.
  8338. --
  8339.  
  8340. function segments(path)
  8341. check('string',path)
  8342. local t = {}
  8343. local index, newindex, elt = 1
  8344. repeat
  8345. newindex = path:find(".", index, true) or #path+1 --last round
  8346. elt = path:sub(index, newindex-1)
  8347. elt = tonumber(elt) or elt
  8348. if elt and elt ~= "" then table.insert(t, elt) end
  8349. index = newindex+1
  8350. until newindex==#path+1
  8351. return t
  8352. end
  8353.  
  8354. --------------------------------------------------------------------------------
  8355. -- Retrieves the element in a sub-table corresponding to the path.
  8356. --
  8357. -- @function [parent=#utils.path] find
  8358. -- @param t is the table to look into.
  8359. -- @param path can be either a string (see @{segments}) or an array where
  8360. -- `path[1]` is the root and `path[n]` is the leaf.
  8361. -- @param force parameter allows to create intermediate tables as specified
  8362. -- by the path, if necessary.
  8363. -- @return returned values depend on force value:
  8364. --
  8365. -- * if force is false (or nil), find returns the table if it finds one,
  8366. -- or it returns nil followed by the subpath that points to non table value
  8367. --
  8368. -- * if force is true, find overwrites or create tables as necessary so
  8369. -- it always returns a table.
  8370. --
  8371. -- * if force is 'noowr', find creates tables as necessary but does not
  8372. -- overwrite non-table values. So as with `force=false`, it only returns a
  8373. -- table if possible, and nil followed by the path that points to the first
  8374. -- neither-table-nor-nil value otherwise.
  8375. --
  8376. -- @usage config = {toto={titi={tutu = 5}}}
  8377. -- find(config, "toto.titi") -- will return the table titi
  8378. --
  8379. function find(t, path, force)
  8380. check('table,string|table', t,path)
  8381. path = type(path)=="string" and segments(path) or path
  8382. for i, n in ipairs(path) do
  8383. local v = t[n]
  8384. if type(v) ~= "table" then
  8385. if not force or (force=="noowr" and v~=nil) then return nil, pathconcat(path, 1, i)
  8386. else v = {} t[n] = v end
  8387. end
  8388. t = v
  8389. end
  8390. return t
  8391. end
  8392.  
  8393. --public API
  8394. M.split=split; M.clean=clean; M.segments=segments; M.get=get; M.set=set;
  8395. M.gsplit=gsplit; M.concat=concat; M.find=find
  8396.  
  8397. return M
  8398. ]===================],"CoolisOS/bin/utils/print.lua",[===================[
  8399. local io=io
  8400. local string=string
  8401. local table=table
  8402. local type=type
  8403. local tostring=tostring
  8404. local pairs=pairs
  8405. local print=print
  8406. local getmetatable=getmetatable
  8407. local select=select
  8408.  
  8409. env=getfenv()
  8410. setmetatable(env,nil)
  8411.  
  8412. mtostring=function(...)
  8413. local t={}
  8414. for i=1,select('#',...) do
  8415. table.insert(t,tostring(select(i,...)))
  8416. end
  8417. return table.concat(t,';')
  8418. end
  8419.  
  8420. -- Make sure stdout is actually flushed every line
  8421. if io and io.stdout then
  8422. io.stdout:setvbuf("line")
  8423. end
  8424.  
  8425. local function quotestring(x)
  8426. local function q(k) return
  8427. k=='"' and '\\"' or
  8428. k=='\\' and '\\\\' or
  8429. string.format('\\%03d', k :byte())
  8430. end
  8431. return '"' .. x :gsub ('[%z\1-\9\11\12\14-\31\128-\255"\\]', q) .. '"'
  8432. end
  8433.  
  8434. ------------------------------------------------------------------------------
  8435. -- virtual printer: print a textual description of the objects in `...', using
  8436. -- function `write()' as a writer. By providing the appropriate writer, one
  8437. -- can make this function write on a TCP channel, in a string,... anywhere.
  8438. -- print_indent is the number of indentation characters used to render
  8439. -- tables. If nil/false, tables are rendered without carriage returns.
  8440. ------------------------------------------------------------------------------
  8441. function vprint (write, print_indent, ...)
  8442. local in_progress = { }
  8443. local indent_level = 0
  8444. local tinsert = table.insert
  8445. local function aux(x)
  8446. local t=type(x)
  8447. if t=="string" then write(quotestring(x))
  8448. elseif t=="boolean" then write(x and "true" or "false")
  8449. elseif t=="number" then write(tostring(x))
  8450. elseif t=="nil" then write("nil")
  8451. elseif t=="table" then
  8452. if getmetatable(x) and getmetatable(x).__tostring then write(tostring(x))
  8453. elseif in_progress[x] then write(tostring(table)) else
  8454. in_progress[x] = true
  8455. indent_level = indent_level+1
  8456.  
  8457. local function print_separator()
  8458. if print_indent then write(",\r\n" .. (" "):rep(print_indent * indent_level))
  8459. else write(", ") end
  8460. end
  8461.  
  8462. -- number of elements according to pairs(): zero, one, many
  8463. local at_least_one, more_than_one = false, false
  8464. for _ in pairs(x) do
  8465. if at_least_one then more_than_one=true; break
  8466. else at_least_one=true end
  8467. end
  8468.  
  8469. if not at_least_one then write "{ }"; return
  8470. elseif not more_than_one or not print_indent then write "{ "
  8471. else write("{\r\n" .. (" "):rep(print_indent * indent_level)) end
  8472.  
  8473. local list_keys = { }
  8474. local last_list_key = 0
  8475. repeat
  8476. local i = last_list_key + 1
  8477. local occupied = x[i]~=nil
  8478. if occupied then list_keys[i]=true; last_list_key=i end
  8479. until not occupied
  8480.  
  8481. for i=1, last_list_key do
  8482. if i>1 then print_separator() end
  8483. aux(x[i])
  8484. end
  8485.  
  8486. local first_hash_pair = true
  8487. for k, v in pairs(x) do
  8488. if not list_keys[k] then -- don't reprint the list-part
  8489. -- separator
  8490. if not first_hash_pair or last_list_key~=0 then print_separator() end
  8491. first_hash_pair = false
  8492. -- key
  8493. if type(k)=="string" and k:match"^[%a_][%w_]*$" then write(k .. " = ")
  8494. else write("["); aux(k); write("] = ") end
  8495. -- value
  8496. aux(v)
  8497. end
  8498. end
  8499. write (" }")
  8500.  
  8501. indent_level = indent_level-1
  8502. in_progress[x] = nil
  8503. end
  8504. else write(tostring(x)) end
  8505. end
  8506. local args = {...}
  8507. local nb = table.getn(args)
  8508. for k = 1, nb do aux(args[k]); if k<nb then write "\t" end end
  8509. end
  8510.  
  8511. ------------------------------------------------------------------------------
  8512. -- A more precise print (on stdout)
  8513. ------------------------------------------------------------------------------
  8514. function p (...)
  8515. local out = {}
  8516. local function write(s)
  8517. table.insert(out, s)
  8518. end
  8519. vprint(write, 3, ...)
  8520. print(table.concat(out))
  8521. end
  8522.  
  8523. ------------------------------------------------------------------------------
  8524. -- Build up and return a string instead of printing in some channel.
  8525. ------------------------------------------------------------------------------
  8526. function siprint(indent, ...)
  8527. local ins, acc = table.insert, { }
  8528. vprint(function(x) return ins(acc, x) end, indent, ...)
  8529. return table.concat(acc)
  8530. end
  8531.  
  8532. function sprint(...)
  8533. return siprint(false, ...)
  8534. end
  8535.  
  8536. function printf(...) return print(string.format(...)) end
  8537.  
  8538. return env]===================],"CoolisOS/bin/utils/serpent.lua",[===================[local n, v = "serpent", 0.224 -- (C) 2012-13 Paul Kulchenko; MIT License
  8539. local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
  8540. local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
  8541. local badtype = {thread = true, userdata = true}
  8542. local keyword, globals, G = {}, {}, (_G or _ENV)
  8543. for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
  8544. 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
  8545. 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
  8546. for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
  8547. for _,g in ipairs({'coroutine', 'io', 'math', 'string', 'table', 'os'}) do
  8548. for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end
  8549.  
  8550. local function s(t, opts)
  8551. local name, indent, fatal,dname = opts.name, opts.indent, opts.fatal,opts.debug
  8552. local sparse, custom, huge,dbg = opts.sparse, opts.custom, not opts.nohuge,opts.debug
  8553. local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
  8554. local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
  8555. local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
  8556. local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
  8557. -- tostring(val) is needed because __tostring may return a non-string value
  8558. function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end)) end
  8559. local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s)
  8560. or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
  8561. or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
  8562. local function comment(s,l)
  8563. return comm and (l or 0) < comm and ' --[['..(dbg and type(s)~='string' and debug.getinfo(s) or tostring(s))..']]' or '' end
  8564. local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
  8565. and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
  8566. local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
  8567. local n = name == nil and '' or name
  8568. local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
  8569. local safe = plain and n or '['..safestr(n)..']'
  8570. return (path or '')..(plain and path and '.' or '')..safe, safe end
  8571. local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding
  8572. local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
  8573. local function padnum(d) return ("%0"..maxn.."d"):format(d) end
  8574. table.sort(k, function(a,b)
  8575. -- sort numeric keys first: k[key] is non-nil for numeric keys
  8576. return (k[a] and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
  8577. < (k[b] and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
  8578. local function val2str(t, name, indent, insref, path, plainindex, level,pass)
  8579. local ttype, level, mt = type(t), (level or 0), getmetatable(t)
  8580. local spath, sname = safename(path, name)
  8581. local tag = plainindex and
  8582. ((type(name) == "number") and '' or name..space..'='..space) or
  8583. (name ~= nil and sname..space..'='..space or '')
  8584. if seen[t] then -- already seen this element
  8585. if dbg then return tag..comment(t,level) end
  8586. table.insert(sref, spath..space..'='..space..seen[t])
  8587. return tag..'nil'..comment('ref'..tostring(t), level) end
  8588. if mt and not dbg then
  8589. if mt.__serialize or mt.__tostring then -- knows how to serialize itself
  8590. seen[t] = insref or spath
  8591. if rawget(mt,'__serialize') then t = mt.__serialize(t) else t = tostring(t) end
  8592. ttype = type(t) -- new value falls through to be serialized
  8593. end
  8594. end
  8595. if ttype == "table" then
  8596. if level >= maxl then return tag..'{}'..comment('max', level) end
  8597. seen[t] = insref or spath
  8598. if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
  8599. local maxn, o, out = #t, {}, {}
  8600. for key = 1, maxn do table.insert(o, key) end
  8601. for key in pairs(t) do if not o[key] then table.insert(o, key) end end
  8602. if opts.sortkeys then alphanumsort(o, t, opts.sortkeys) end
  8603. for n, key in ipairs(o) do
  8604. local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
  8605. if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing
  8606. or opts.keyallow and not opts.keyallow[key]
  8607. or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types
  8608. or sparse and value == nil then -- skipping nils; do nothing
  8609. elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then
  8610. if dbg then
  8611. local s
  8612. if not (seen[key] or globals[key]) then
  8613. seen[key]=debug.getinfo(key)
  8614. s = '['..(val2str(key,nil,indent,nil,nil,false,level+1))..']'..comment(key,level)
  8615.  
  8616. end
  8617. table.insert(out,val2str(value,s or (seen[key] or globals[key]),indent,insref,nil,true,level+1))
  8618. else
  8619. if not seen[key] and not globals[key] then
  8620. local sname = safename(iname, gensym(key)) -- iname is table for local variables
  8621. sref[#sref] = val2str(key,sname,indent,sname,iname,true)
  8622. end
  8623. table.insert(sref, 'placeholder')
  8624. local path = seen[t]..'['..(seen[key] or globals[key] or gensym(key))..']'
  8625. sref[#sref] = path..space..'='..space..(seen[value] or val2str(value,nil,indent,path))
  8626. end
  8627. else
  8628. table.insert(out,val2str(value,key,indent,insref,seen[t],plainindex,level+1))
  8629. end
  8630. end
  8631. local prefix = string.rep(indent or '', level)
  8632. local head = indent and '{\n'..prefix..indent or '{'
  8633. local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
  8634. local tail = indent and "\n"..prefix..'}' or '}'
  8635. return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level)
  8636. elseif badtype[ttype] then
  8637. seen[t] = insref or spath
  8638. return tag..globerr(t, level)
  8639. elseif ttype == 'function' and not dbg then
  8640. seen[t] = insref or spath
  8641. local ok, res = pcall(string.dump, t)
  8642. local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or
  8643. "loadstring("..safestr(res)..",'@serialized')")..comment(t, level))
  8644. return tag..(func or globerr(t, level))
  8645. else return tag..safestr(t) end -- handle all other types
  8646. end
  8647. local sepr = indent and "\n" or ";"..space
  8648. local body = val2str(t, name, indent) -- this call also populates sref
  8649. local tail = #sref>1 and table.concat(sref, sepr)..sepr or ''
  8650. return not name and body or "do local "..body..sepr..tail.."return "..name..sepr.."end"
  8651. end
  8652.  
  8653. 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
  8654. return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
  8655. dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
  8656. line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
  8657. block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }]===================],"CoolisOS/bin/utils/turtle.lua",[===================[
  8658.  
  8659. --[[
  8660. process:
  8661. -state
  8662. -state handler
  8663. --interrupts
  8664. -state machines?
  8665. -complex for generalism
  8666. -simple for dig queues: <- are generally stateless
  8667. are kept separately, dig queues are just vector coords
  8668. position and orientation->state
  8669. turtle api overwritten to catch movement
  8670. interpreter command to set position, world-wide
  8671. implies keepig initial referencial
  8672. simple wrap-up of goto
  8673. (need stack like state machines for pausing, and then returning)
  8674. -general pathfinding
  8675. djikstra algorithm
  8676. maintainig map
  8677. - imprredictable movement
  8678. + can contornate obstacles
  8679. ]]
  8680. --state_stack={}
  8681. --
  8682. --
  8683. --
  8684. --function get_move_queue()
  8685. -- local q={}
  8686. -- for i=1,10 do
  8687. -- table.insert(function() goto(Vec3(i,0,0)) end)
  8688. -- end
  8689. -- return q
  8690. --end
  8691. --
  8692. --[[doing queue implies
  8693. --getting state/resuming state
  8694. --executing and saving <- open new, save current, delete old
  8695. --goto needs some context
  8696. --turtle movement is single -coroutined; means that mode can be global
  8697. --]]
  8698. --function get_coords()
  8699. -- f=fs.open('ect/pos','r')
  8700. -- pos=Vec3(textutils.deserialize(f.readAll()))
  8701. -- f.close()
  8702. -- return pos
  8703. --end
  8704. --function save_pos(pos)
  8705. -- f=fs.open('ect/pos','w')
  8706. -- f.writeLine(pos:serialize())
  8707. -- f.close()
  8708. --end
  8709. --
  8710. --
  8711. --function get_state_for(queue)
  8712. -- local coords=get_coords()
  8713. -- for i,v in pairs(queue) do
  8714. -- if v==coords then return i end
  8715. -- end
  8716. --end
  8717. --function do_queue(queue)
  8718. -- cur=get_state_for(queue)
  8719. -- for i=cur,#queue do
  8720. -- goto(queue[i])
  8721. -- end
  8722. --end
  8723. --
  8724. --function main()
  8725. -- mov_queue=get_move_queue()
  8726. -- do_queue(queue)
  8727. --end
  8728.  
  8729. if not turtle then error('not a turtle',2) end
  8730.  
  8731. if turtle._old then
  8732. turtle=util.shcopy(turtle._old)
  8733. else
  8734. turtle._old=util.shcopy(turtle)
  8735. end
  8736.  
  8737. local _log=log
  8738. local log=function(...) return _log('turtle',...) end
  8739.  
  8740. include'kernel.class'
  8741. local Vec3=require'utils.Vec3'
  8742.  
  8743. local util=require'kernel.util'
  8744. local Var=require'kernel.class.Var'
  8745.  
  8746. ---forward declaration
  8747. local api
  8748.  
  8749. --persistence
  8750. local pos,dir
  8751. local saveDir=fs.combine(_INSTALL_PATH,'ect/turtle')
  8752. if not fs.exists(saveDir) then fs.makeDir(saveDir) end
  8753.  
  8754. local dump=require'utils.serpent'.dump
  8755. local var=Var(fs.combine(saveDir,'pos-dir.var'))
  8756. local mode=Var(fs.combine(saveDir,'mode.var'))
  8757.  
  8758. local function merge(dst,src)
  8759. for i,v in pairs(src) do
  8760. if dst[i]==nil then
  8761. dst[i]=src[i]
  8762. end
  8763. end
  8764. end
  8765. merge(mode,{
  8766. dig=true,
  8767. attack=true,
  8768. count=20,
  8769. delay=0.5,
  8770. gps=false,
  8771. })
  8772.  
  8773.  
  8774. local function save()
  8775. var.pos=pos
  8776. if dir==Vec3() then error'zero dir' end
  8777. var.dir=dir
  8778. end
  8779. local function load()
  8780. pos,dir=Vec3(var.pos),Vec3(var.dir)
  8781. end
  8782.  
  8783. function set(_pos,_dir)
  8784. pos,dir=(_pos or pos):clone(),(_dir or dir):clone()
  8785. save()
  8786. end
  8787. function get() return pos:clone(),dir:clone() end
  8788.  
  8789. local function sign(num)
  8790. return (num==nil or num==0) and 0 or (num>0 and 1 or -1)
  8791. end
  8792. --fuel handling
  8793. function ensureFuel()
  8794. if api.ensureFuel then api.ensureFuel() end
  8795. end
  8796. ---
  8797. --atomic overrides of the turtle api and moves def
  8798. moves={}
  8799. do
  8800. local s_dirs={
  8801. up={
  8802. vec=function() return Vec3(0,1,0) end,
  8803. at=function(at)
  8804. at.y=at.y+1
  8805. end,
  8806. raw_name="up",
  8807. attack=turtle.attackUp,
  8808. suck=turtle.suckUp,dig=turtle.digUp, raw_move=turtle.up, detect=turtle.detectUp, place=turtle.placeUp,compare=turtle.compareUp, drop=turtle.dropUp,},
  8809. forward={
  8810. vec=function() return Vec3(dir.x,0,dir.z) end,
  8811. at=function(at,dir)
  8812. at.x=at.x+dir.x
  8813. at.z=at.z+dir.z
  8814. end,
  8815. attack=turtle.attack,
  8816. 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,},
  8817. down={
  8818. vec=function() return Vec3(0,-1,0) end,
  8819. at=function(at)
  8820. at.y=at.y-1
  8821. end,
  8822. attack=turtle.attackDown,
  8823. 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,},
  8824. back={
  8825. vec=function() return Vec3(-dir.x,0,-dir.z) end,
  8826. at=function(at,dir)
  8827. at.x=at.x-dir.x
  8828. at.z=at.z-dir.z
  8829. end,
  8830. raw_name='back',raw_move=turtle.back},
  8831. }
  8832. local t_dirs={
  8833. left={
  8834. vec=function() return Vec3(dir.z,0,-dir.x) end,
  8835. raw_name='turnLeft',raw_move=turtle.turnLeft,at=function(at,dir) dir.x, dir.z = dir.z, -dir.x end },
  8836. right={
  8837. vec=function() return Vec3(-dir.z,0,dir.x) end,
  8838. raw_name='turnRight',raw_move=turtle.turnRight,at=function(at,dir) dir.x, dir.z = -dir.z, dir.x end},
  8839. }
  8840. local opp={
  8841. up='down',
  8842. forward='back',
  8843. right='left',
  8844. }
  8845. for i,v in pairs(opp) do opp[v]=i end
  8846.  
  8847. local function helpMove(doF,moveF,undoF,fuel)
  8848. if fuel then ensureFuel() end
  8849. doF(pos,dir)
  8850. save()
  8851. local r=moveF()
  8852. if not r then undoF(pos,dir) save() end
  8853. return r
  8854. end
  8855.  
  8856. for mv,t in pairs(s_dirs) do
  8857. local args={t.at,t.raw_move,s_dirs[opp[mv]].at,true}
  8858. turtle[t.raw_name]=function() return helpMove(unpack(args)) end
  8859. t.move=turtle[t.raw_name]
  8860. moves[mv]=t
  8861. end
  8862. for mv,t in pairs(t_dirs) do
  8863. local args={t.at,t.raw_move,t_dirs[opp[mv]].at}
  8864. turtle[t.raw_name]=function() return helpMove(unpack(args)) end
  8865. t.move=turtle[t.raw_name]
  8866. moves[mv]=t
  8867. end
  8868.  
  8869. --aliases
  8870. -- local function tdo(turnF,doF) return function(...) turnF() return doF(...) end end
  8871.  
  8872. -- moves.around={}
  8873. -- util.shcopy({
  8874. -- suck=tdo(moves.around,forward.suck),
  8875. -- attack=tdo(moves.around,forward.attack),
  8876. -- drop=tdo(moves.around,forward.drop),
  8877. -- move=function() turtle.turnLeft() turtle.turnLeft() end,
  8878. -- at=function(at,dir) moves.left.at(at,dir) moves.left.at(at,dir) end,
  8879. -- },moves.around)
  8880.  
  8881.  
  8882.  
  8883. for i,v in pairs(moves) do
  8884. setmetatable(v,
  8885. {__call=function(t,...)
  8886. return v.move(...)
  8887. end,
  8888. })
  8889. end
  8890. moves.top=moves.up
  8891. moves.bottom=moves.down
  8892. moves.front=moves.forward
  8893.  
  8894.  
  8895.  
  8896. end
  8897. for i,v in pairs(moves) do
  8898. local env=(getfenv())
  8899. env[i]=v
  8900. end
  8901. ---
  8902.  
  8903. --world pos related
  8904. compass={
  8905. {name='north',n=0,vec=function() return Vec3( 0, 0,-1) end},
  8906. {name='east',n=1,vec=function() return Vec3( 1, 0, 0) end},
  8907. {name='south',n=2,vec=function() return Vec3( 0, 0, 1) end},
  8908. {name='west',n=3,vec=function() return Vec3(-1, 0, 0) end},
  8909. }
  8910. for i,v in pairs(compass) do compass[v.name]=v end
  8911.  
  8912. --vec, mov, string, or nil
  8913. function getFacing(mv)
  8914. local vec
  8915. if mv==nil then vec=dir
  8916. elseif classof(mv)==Vec3 then vec=mv
  8917. elseif type(mv)=='string' then vec=moves[mv].vec()
  8918. else error() end
  8919. for i,c in ipairs(compass) do
  8920. if vec==c.vec() then return c.name end
  8921. end
  8922. end
  8923. function getChunk(v)
  8924. v=v or pos
  8925. return math.floor(v.x/16), math.floor(v.z/16)
  8926. end
  8927. function getChunkPosition(v)
  8928. v=v or pos
  8929. return v.x%16, v.z%16
  8930. end
  8931. ---
  8932.  
  8933. --[[input patterns:
  8934. x,y,z ->vector
  8935. string->move or compass
  8936. move or compas->vec
  8937.  
  8938. drop
  8939. suck
  8940. place
  8941.  
  8942. ]]
  8943. --arg transformer
  8944. local function trf(...)
  8945. if classof(...)==Vec3 then
  8946. return ...
  8947. elseif type(...)=='string' then
  8948. if moves[...] then
  8949. return moves[...].vec()
  8950. elseif compass[...] then
  8951. return compass[...].vec()
  8952. end
  8953. else return Vec3(...) end
  8954. end
  8955. --public api
  8956. --mv based
  8957. local lastMoveNeededDig=false
  8958.  
  8959. function clear(mv)
  8960. mv=type(mv)=='string' and moves[mv] or mv
  8961. local maxCount=mode.count
  8962. local delay=mode.delay
  8963.  
  8964. local dig_count=0
  8965. local count=0
  8966. while (mv.detect()) and (count < maxCount) do
  8967. if(count == 0) then
  8968. -- Am about to dig a block - check that it is not a chest if necessary
  8969. -- If we are looking for chests, then check that this isn't a chest before moving
  8970. if not mode.dig then
  8971. return false,'dig'
  8972. end
  8973. end
  8974. dig_count = (mv.dig() and 1 or 0)+count
  8975. count=count+1
  8976. sleep(delay)
  8977. end
  8978. if dig_count~=0 then lastMoveNeededDig=true end
  8979. return (count~=maxCount),count,dig_count
  8980. end
  8981. function force_back()
  8982. if not moves.back() then
  8983. if move'back' then
  8984. turn'back'
  8985. return true,true
  8986. else
  8987. return false
  8988. end
  8989. end
  8990. return true
  8991. end
  8992.  
  8993. local function _force(mv,key)
  8994. mv=type(mv)=='string' and moves[mv] or mv
  8995. if mv==moves.back then return force_back(mv,key) end
  8996. local suc
  8997. if not lastMoveNeededDig then
  8998. suc=mv[key]()
  8999. if suc then return true,0,0 end
  9000. end
  9001. local maxCount=mode.count
  9002. local delay=mode.delay
  9003.  
  9004. local dig_count=0
  9005. local count=0
  9006. while (not suc) and (count < maxCount) do
  9007. if mv.detect() then
  9008. if(count == 0) then
  9009. -- Am about to dig a block - check that it is not a chest if necessary
  9010. -- If we are looking for chests, then check that this isn't a chest before moving
  9011. if not mode.dig then
  9012. return false,'dig'
  9013. end
  9014. end
  9015. dig_count = (mv.dig() and 1 or 0)+count
  9016. else
  9017. if not mode.attack then
  9018. return false,'attack'
  9019. end
  9020. mv.attack()
  9021. end
  9022. sleep(delay)
  9023. suc=mv[key]()
  9024. count=count+1
  9025. end
  9026. if dig_count~=0 then lastMoveNeededDig=true end
  9027. return suc,count,dig_count
  9028. end
  9029. function force(mv)
  9030. return _force(mv,'move')
  9031. end
  9032. function place(mv)
  9033. return _force(mv,'place')
  9034. end
  9035. function advance(mv, steps)
  9036. mv=type(mv)=='string' and moves[mv] or mv
  9037. steps = steps or 1
  9038. for step = 1,steps do
  9039. force(mv)
  9040. end
  9041. end
  9042.  
  9043. --vector based
  9044. function turn(...)
  9045. local to=trf(...)
  9046. local x,z = sign(to.x), sign(to.z)
  9047. if x == dir.x and z == dir.z then
  9048. return
  9049. elseif (x == -dir.x and x~=0) or (z == -dir.z and z~=0) then --or?
  9050. left()
  9051. left()
  9052. elseif x ~= 0 then
  9053. if x == -dir.z then right() else left() end
  9054. elseif z ~= 0 then
  9055. if z == dir.x then right() else left() end
  9056. end
  9057. end
  9058.  
  9059. function goto(...)
  9060. local to=trf(...)
  9061. if to.y>pos.y then
  9062. advance(up, to.y-pos.y)
  9063. elseif to.y<pos.y then
  9064. advance(down, pos.y-to.y)
  9065. end
  9066. if to.x ~= pos.x then
  9067. turn({x=to.x-pos.x})
  9068. advance(forward, math.abs(to.x-pos.x))
  9069. end
  9070. if to.z ~= pos.z then
  9071. turn({z=to.z-pos.z})
  9072. advance(forward, math.abs(to.z-pos.z))
  9073. end
  9074. end
  9075.  
  9076. function move(...)
  9077. local to=trf(...)
  9078. return goto(to+pos)
  9079. end
  9080. ---
  9081. function debug()
  9082. return ({lastMoveNeededDig=lastMoveNeededDig,pos_dir={get()}})
  9083. end
  9084. --init code
  9085. local function update()
  9086. load()
  9087. pos,dir=(pos or Vec3()),(dir or compass.south.vec())
  9088. save()
  9089. if mode.gps then
  9090. local ok, pos_,dir_=pcall(getGps)
  9091. if ok then set(pos_,dir_)
  9092. else log('WARNING','Could not use gps:',pos_) end
  9093. end
  9094. end
  9095.  
  9096. function getGps()
  9097. rednet.open'right'
  9098. local x,y,z=gps.locate(5)
  9099. if x then
  9100. gps_pos=Vec3(x,y,z)
  9101. local org=(get())
  9102. for _,c in ipairs(compass) do
  9103. if goto(c.vec()+org) then
  9104. local x,y,z=gps.locate(5)
  9105. if x then
  9106. local gps_pos2=Vec3(x,y,z)
  9107. local dir=gps_pos2-gps_pos
  9108. if dir~=Vec3(0,0,0) then
  9109. return true,gps_pos2,dir
  9110. end
  9111. end
  9112. end
  9113. end
  9114. error'blocked'
  9115. else
  9116. error'no gps signal'
  9117. end
  9118. end
  9119. ---
  9120.  
  9121. update()
  9122.  
  9123.  
  9124.  
  9125. api={
  9126. debug=debug,
  9127.  
  9128. set=set,
  9129. get=get,
  9130.  
  9131. moves=moves,
  9132. front=moves.forward,
  9133. bottom=moves.down,
  9134. top=moves.up,
  9135.  
  9136. clear=clear,
  9137. force=force,
  9138. place=place,
  9139. advance=advance,
  9140.  
  9141. mode=mode,
  9142.  
  9143. getFacing=getFacing,
  9144. getChunk=getChunk,
  9145. getChunkPos=getChunkPos,
  9146. compass=compass,
  9147.  
  9148. getGps=getGps,
  9149.  
  9150. turn=turn,
  9151. move=move,
  9152. goto=goto,
  9153. }
  9154. for i,v in pairs(moves) do api[i]=v end
  9155. api.api=api
  9156.  
  9157. return api]===================],"CoolisOS/bin/utils/Vec3.lua",[===================[--[[
  9158. Advanced Vector API by Tomass1996
  9159.  
  9160. Vector 'Object' Functions And Fields:
  9161. vector.x -- Vectors X component
  9162. vector.y -- Vectors Y component
  9163. vector.z -- Vectors Z component
  9164. vector:add(otherVector) -- Component-wise addition
  9165. vector:scalarAdd(n) -- Scalar addition
  9166. vector:subtract(otherVector) -- Component-wise subtraction
  9167. vector:scalarSubtract(n) -- Scalar subtraction
  9168. vector:eldot(otherVector) -- Component-wise multiplication
  9169. vector:scalarMultiply(n) -- Scalar multiplication
  9170. vector:divide(otherVector) -- Component-wise division
  9171. vector:scalarDivide(n) -- Scalar division
  9172. vector:length() -- Get the length of the vector
  9173. vector:lengthSq() -- Get the length ^ 2 of the vector
  9174. vector:distance(otherVector) -- Get the distance away from a vector
  9175. vector:distanceSq(otherVector) -- Get the distance away from a vector, squared
  9176. vector:normalize() -- Get the normalized vector
  9177. vector:dot(otherVector) -- Get the dot product of vector and otherVector
  9178. vector:cross(otherVector) -- Get the cross product of vector and otherVector
  9179. vector:containedWithin(minVec, maxVec)-- Check to see if vector is contained within minVec and maxVec
  9180. vector:clampX(min, max) -- Clamp the X component
  9181. vector:clampY(min, max) -- Clamp the Y component
  9182. vector:clampZ(min, max) -- Clamp the Z component
  9183. vector:floor() -- Rounds all components down
  9184. vector:ceil() -- Rounds all components up
  9185. vector:round() -- Rounds all components to the closest integer
  9186. vector:absolute() -- Vector with absolute values of components
  9187. vector:isCollinearWith(otherVector) -- Checks to see if vector is collinear with otherVector
  9188. vector:getIntermediateWithX(other, x) -- New vector with given x value along the line between vector and other, or nil if not possible
  9189. vector:getIntermediateWithY(other, y) -- New vector with given y value along the line between vector and other, or nil if not possible
  9190. vector:getIntermediateWithZ(other, z) -- New vector with given z value along the line between vector and other, or nil if not possible
  9191. vector:rotateAroundX(angle) -- Rotates vector around the x axis by the specified angle(radians)
  9192. vector:rotateAroundY(angle) -- Rotates vector around the y axis by the specified angle(radians)
  9193. vector:rotateAroundZ(angle) -- Rotates vector around the z axis by the specified angle(radians)
  9194. vector:clone() -- Returns a new vector with same component values as vector
  9195. vector:equals(otherVector) -- Checks to see if vector and otherVector are equal
  9196. vector:tostring() -- Returns the string representation of vector "(x, y, z)"
  9197.  
  9198. Vector 'Object' Metatable Overrides: -- [x, y, z] represents a vector object in these examples, not irl
  9199. To String -- tostring will get the string representation
  9200. ie. tostring([1, 2, 3]) --> "(1, 2, 3)"
  9201. Unary Minus -- Using unary minus on a vector will result in the negative of vector
  9202. ie. -[1, -2, 3] --> [-1, 2, -3]
  9203. Addition -- Can add two vectors or vector and number with +
  9204. ie. [1, 2, 3] + [4, 5, 6] --> [5, 7, 9]
  9205. [1, 2, 3] + 3 --> [4, 5, 6]
  9206. Subtraction -- Can subtract two vectors or vector and number with -
  9207. ie. [4, 5, 6] - [1, 2, 3] --> [3, 3, 3]
  9208. [4, 5, 6] - 3 --> [1, 2, 3]
  9209. Multiplication -- Can dot two vectors or vector and number with *
  9210. ie. [1, 2, 3] * [4, 5, 6] --> [4, 10, 18]
  9211. [1, 2, 3] * 3 --> [3, 6, 9]
  9212. Division -- Can divide two vectors or vector and number with /
  9213. ie. [4, 10, 18] / [1, 2, 3] --> [4, 5, 6]
  9214. [3, 6, 9] / 3 --> [1, 2, 3]
  9215. Equality -- Can check if two vectors are the same with ==
  9216. ie. [4, 5, 6] == [4, 5, 6] --> true
  9217. [4, 5, 6] == [4, 99, 6] --> false
  9218.  
  9219. Vector API functions:
  9220. Vector.getMinimum(v1, v2) -- Gets the minimum components of two vectors
  9221. Vector.getMaximum(v1, v2) -- Gets the maximum components of two vectors
  9222. Vector.getMidpoint(v1, v2) -- Gets the midpoint of two vectors
  9223. Vector.isVector(v) -- Checks whether v is a vector created by this api
  9224. Vector(x, y, z) -- Creates a new vector object with the component values
  9225. --]]
  9226.  
  9227. include'kernel.class'
  9228. class.Vec3()
  9229. function Vec3:__init(t,y,z)
  9230. if type(t)=='table' then
  9231. self.x = t.x or t[1] or 0
  9232. self.y = t.y or t[2] or 0
  9233. self.z = t.z or t[3] or 0
  9234. else
  9235. self.x = t or 0
  9236. self.y = y or 0
  9237. self.z = z or 0
  9238. end
  9239. end
  9240. local vector=Vec3
  9241. local new=Vec3
  9242. local isVector=function(v) return classof(v)==Vec3 end
  9243. local getType=ofType
  9244. local vector = {
  9245. add = function(self, v)
  9246. self.x=self.x + v.x
  9247. self.y=self.y + v.y
  9248. self.z=self.z + v.z
  9249. return self
  9250. end,
  9251. scalarAdd = function(self, n)
  9252. self.x=self.x + n
  9253. self.y=self.y + n
  9254. self.z=self.z + n
  9255. return self
  9256. end,
  9257. subtract = function(self, v)
  9258. self.x=self.x - v.x
  9259. self.y=self.y - v.y
  9260. self.z=self.z - v.z
  9261. return self
  9262. end,
  9263. scalarSubtract = function(self, n)
  9264. return self:scalarAdd(-n)
  9265. end,
  9266. eldot = function(self, v)
  9267. self.x=self.x * v.x
  9268. self.y=self.y * v.y
  9269. self.z=self.z * v.z
  9270. return self
  9271. end,
  9272. scalarMultiply = function(self, n)
  9273. self.x=self.x * n
  9274. self.y=self.y * n
  9275. self.z=self.z * n
  9276. return self
  9277. end,
  9278. divide = function(self, o)
  9279. return new(
  9280. self.x / o.x,
  9281. self.y / o.y,
  9282. self.z / o.z
  9283. )
  9284. end,
  9285. scalarDivide = function(self, n)
  9286. return new(
  9287. self.x / n,
  9288. self.y / n,
  9289. self.z / n
  9290. )
  9291. end,
  9292. length = function(self)
  9293. return math.sqrt(
  9294. self.x * self.x
  9295. + self.y * self.y
  9296. + self.z * self.z
  9297. )
  9298. end,
  9299. lengthSq = function(self)
  9300. return (
  9301. self.x * self.x
  9302. + self.y * self.y
  9303. + self.z * self.z
  9304. )
  9305. end,
  9306. distance = function(self, o)
  9307. return math.sqrt(
  9308. math.pow(o.x - self.x, 2)
  9309. + math.pow(o.y - self.y, 2)
  9310. + math.pow(o.z - self.z, 2)
  9311. )
  9312. end,
  9313. distanceSq = function(self, o)
  9314. return (
  9315. math.pow(o.x - self.x, 2)
  9316. + math.pow(o.y - self.y, 2)
  9317. + math.pow(o.z - self.z, 2)
  9318. )
  9319. end,
  9320. normalize = function(self)
  9321. return self:scalarDivide(self:length())
  9322. end,
  9323. dot = function(self, o)
  9324. return (
  9325. self.x * o.x
  9326. + self.y * o.y
  9327. + self.z * o.z
  9328. )
  9329. end,
  9330. cross = function(self, o)
  9331. return new(
  9332. self.y * o.z - self.z * o.y,
  9333. self.z * o.x - self.x * o.z,
  9334. self.x * o.y - self.y * o.x
  9335. )
  9336. end,
  9337. containedWithin = function(self, min, max)
  9338. return (
  9339. self.x >= min.x and self.x <= max.x
  9340. and self.y >= min.y and self.y <= max.y
  9341. and self.z >= min.z and self.z <= max.z
  9342. )
  9343. end,
  9344. clampX = function(self, min, max)
  9345. return new(
  9346. math.max(min, math.min(max, self.x)),
  9347. self.y,
  9348. self.z
  9349. )
  9350. end,
  9351. clampY = function(self, min, max)
  9352. return new(
  9353. self.x,
  9354. math.max(min, math.min(max, self.y)),
  9355. self.z
  9356. )
  9357. end,
  9358. clampZ = function(self, min, max)
  9359. return new(
  9360. self.x,
  9361. self.y,
  9362. math.max(min, math.min(max, self.z))
  9363. )
  9364. end,
  9365. floor = function(self)
  9366. return new(
  9367. math.floor(self.x),
  9368. math.floor(self.y),
  9369. math.floor(self.z)
  9370. )
  9371. end,
  9372. ceil = function(self)
  9373. return new(
  9374. math.ceil(self.x),
  9375. math.ceil(self.y),
  9376. math.ceil(self.z)
  9377. )
  9378. end,
  9379. round = function(self)
  9380. return new(
  9381. math.floor(self.x + 0.5),
  9382. math.floor(self.y + 0.5),
  9383. math.floor(self.z + 0.5)
  9384. )
  9385. end,
  9386. absolute = function(self)
  9387. return new(
  9388. math.abs(self.x),
  9389. math.abs(self.y),
  9390. math.abs(self.z)
  9391. )
  9392. end,
  9393. isCollinearWith = function(self, o)
  9394. if self.x == 0 and self.y == 0 and self.z == 0 then
  9395. return true
  9396. end
  9397. local otherX, otherY, otherZ = o.x, o.y, o.z
  9398. if otherX == 0 and otherY == 0 and otherZ == 0 then
  9399. return true
  9400. end
  9401. if (self.x == 0) ~= (otherX == 0) then return false end
  9402. if (self.y == 0) ~= (otherY == 0) then return false end
  9403. if (self.z == 0) ~= (otherZ == 0) then return false end
  9404. local quotientX = otherX / self.x
  9405. if quotientX == quotientX then
  9406. return o:equals(self:scalarMultiply(quotientX))
  9407. end
  9408. local quotientY = otherY / self.y
  9409. if quotientY == quotientY then
  9410. return o:equals(self:scalarMultiply(quotientY))
  9411. end
  9412. local quotientZ = otherZ / self.z
  9413. if quotientZ == quotientZ then
  9414. return o:equals(self:scalarMultiply(quotientZ))
  9415. end
  9416. end,
  9417. getIntermediateWithX = function(self, o, v)
  9418. local vX = o.x - self.x
  9419. local vY = o.y - self.y
  9420. local vZ = o.z - self.z
  9421. if vX * vX < 1.0000000116860974e-7 then
  9422. return nil
  9423. else
  9424. local nMul = (v - self.x) / vX
  9425. return (
  9426. (nMul >= 0 and nMul <= 1)
  9427. and new(
  9428. self.x + vX * nMul,
  9429. self.y + vY * nMul,
  9430. self.z + vZ * nMul
  9431. )
  9432. or nil
  9433. )
  9434. end
  9435. end,
  9436. getIntermediateWithY = function(self, o, v)
  9437. local vX = o.x - self.x
  9438. local vY = o.y - self.y
  9439. local vZ = o.z - self.z
  9440. if vY * vY < 1.0000000116860974e-7 then
  9441. return nil
  9442. else
  9443. local nMul = (v - self.y) / vY
  9444. return (
  9445. (nMul >= 0 and nMul <= 1)
  9446. and new(
  9447. self.x + vX * nMul,
  9448. self.y + vY * nMul,
  9449. self.z + vZ * nMul
  9450. )
  9451. or nil
  9452. )
  9453. end
  9454. end,
  9455. getIntermediateWithZ = function(self, o, v)
  9456. local vX = o.x - self.x
  9457. local vY = o.y - self.y
  9458. local vZ = o.z - self.z
  9459. if vZ * vZ < 1.0000000116860974e-7 then
  9460. return nil
  9461. else
  9462. local nMul = (v - self.z) / vZ
  9463. return (
  9464. (nMul >= 0 and nMul <= 1)
  9465. and new(
  9466. self.x + vX * nMul,
  9467. self.y + vY * nMul,
  9468. self.z + vZ * nMul
  9469. )
  9470. or nil
  9471. )
  9472. end
  9473. end,
  9474. rotateAroundX = function(self, n)
  9475. local c, s = math.cos(n), math.sin(n)
  9476. return new(
  9477. self.x,
  9478. self.y * c + self.z * s,
  9479. self.z * c - self.y * s
  9480. )
  9481. end,
  9482. rotateAroundY = function(self, n)
  9483. local c, s = math.cos(n), math.sin(n)
  9484. return new(
  9485. self.x * c + self.z * s,
  9486. self.y,
  9487. self.z * c - self.x * s
  9488. )
  9489. end,
  9490. rotateAroundZ = function(self, n)
  9491. local c, s = math.cos(n), math.sin(n)
  9492. return new(
  9493. self.x * c + self.y * s,
  9494. self.y * c - self.x * s,
  9495. self.z
  9496. )
  9497. end,
  9498. clone = function(self)
  9499. return Vec3(self)
  9500. end,
  9501. equals = function(self, o)
  9502. if not isVector(self) or not isVector(o) then return false end
  9503. return (
  9504. o.x == self.x
  9505. and o.y == self.y
  9506. and o.z == self.z
  9507. )
  9508. end,
  9509. tostring = function(self)
  9510. return "("..self.x..", "..self.y..", "..self.z..")"
  9511. end,
  9512. getMinimum=function(v1, v2)
  9513. return new(
  9514. math.min(v1.x, v2.x),
  9515. math.min(v1.y, v2.y),
  9516. math.min(v1.z, v2.z)
  9517. )
  9518. end,
  9519. getMidpoint= function(v1, v2)
  9520. return new(
  9521. (v1.x + v2.x) / 2,
  9522. (v1.y + v2.y) / 2,
  9523. (v1.z + v2.z) / 2
  9524. )
  9525. end,
  9526. __serialize=function(self)
  9527. return {self.x,self.y,self.z}
  9528. end
  9529. }
  9530.  
  9531. local vmetatable = {
  9532. __tostring = vector.tostring,
  9533. __unm = function(v) return v:scalarMultiply(-1) end,
  9534. __add = function(a, b)
  9535. if type(b) == "number" and isVector(a) then
  9536. return a:clone():scalarAdd(b)
  9537. elseif type(a) == "number" and isVector(b) then
  9538. return b:clone():scalarAdd(a)
  9539. elseif isVector(a) and isVector(b) then
  9540. return a:clone():add(b)
  9541. else
  9542. error("Attempt to perform vector addition on <"..getType(a).."> and <"..getType(b)..">")
  9543. end
  9544. end,
  9545. __sub = function(a, b)
  9546. if type(b) == "number" and isVector(a) then
  9547. return a:clone():scalarSubtract(b)
  9548. elseif type(a) == "number" and isVector(b) then
  9549. return b:clone():scalarSubtract(a)
  9550. elseif isVector(a) and isVector(b) then
  9551. return a:clone():subtract(b)
  9552. else
  9553. error("Attempt to perform vector subtraction on <"..getType(a).."> and <"..getType(b)..">")
  9554. end
  9555. end,
  9556. __mul = function(a, b)
  9557. if type(b) == "number" and isVector(a) then
  9558. return a:scalarMultiply(b)
  9559. elseif type(a) == "number" and isVector(b) then
  9560. return b:scalarMultiply(a)
  9561. elseif isVector(a) and isVector(b) then
  9562. return a:dot(b)
  9563. else
  9564. error("Attempt to perform vector multiplication on <"..getType(a).."> and <"..getType(b)..">")
  9565. end
  9566. end,
  9567. __div = function(a, b)
  9568. if type(b) == "number" and isVector(a) then
  9569. return a:scalarDivide(b)
  9570. elseif type(a) == "number" and isVector(b) then
  9571. return b:scalarDivide(a)
  9572. elseif isVector(a) and isVector(b) then
  9573. return a:divide(b)
  9574. else
  9575. error("Attempt to perform vector division on <"..getType(a).."> and <"..getType(b)..">")
  9576. end
  9577. end,
  9578. __eq = vector.equals,
  9579. __pow = vector.cross,
  9580. }
  9581.  
  9582.  
  9583.  
  9584. util.shcopy(vector,Vec3)
  9585. util.shcopy(vmetatable,Vec3)
  9586.  
  9587. --[[test
  9588. v=require'bin.utils.Vec3'
  9589.  
  9590.  
  9591. ]]
  9592.  
  9593. return Vec3]===================],"CoolisOS/bin/utils/pl",true,"CoolisOS/bin/utils/pl/List.lua",[===================[--- Python-style list class.
  9594. --
  9595. -- **Please Note**: methods that change the list will return the list.
  9596. -- This is to allow for method chaining, but please note that `ls = ls:sort()`
  9597. -- does not mean that a new copy of the list is made. In-place (mutable) methods
  9598. -- are marked as returning 'the list' in this documentation.
  9599. --
  9600. -- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion}
  9601. --
  9602. -- 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
  9603. --
  9604. -- **Note**: The comments before some of the functions are from the Python docs
  9605. -- and contain Python code.
  9606. --
  9607. -- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan.
  9608. --
  9609. -- Dependencies: `pl.utils`, `pl.tablex`
  9610. -- @module pl.List
  9611. -- @pragma nostrip
  9612.  
  9613. local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort
  9614. local setmetatable, getmetatable,type,tostring,assert,string,next = setmetatable,getmetatable,type,tostring,assert,string,next
  9615. local write = io.write
  9616. local tablex = require 'tablex'
  9617. local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues
  9618. local tablex = tablex
  9619. local tsub = tablex.sub
  9620. local utils = require 'utils'
  9621. local array_tostring,split,is_type,assert_arg,function_arg = utils.array_tostring,utils.split,utils.is_type,utils.assert_arg,utils.function_arg
  9622. local normalize_slice = tablex._normalize_slice
  9623.  
  9624. --[[
  9625. module ('pl.List',utils._module)
  9626. ]]
  9627.  
  9628. local Multimap = utils.stdmt.MultiMap
  9629. -- metatable for our list objects
  9630. local List = utils.stdmt.List
  9631. List.__index = List
  9632. List._class = List
  9633.  
  9634. local iter
  9635.  
  9636. -- we give the metatable its own metatable so that we can call it like a function!
  9637. setmetatable(List,{
  9638. __call = function (tbl,arg)
  9639. return List.new(arg)
  9640. end,
  9641. })
  9642.  
  9643. local function makelist (t,obj)
  9644. local klass = List
  9645. if obj then
  9646. klass = getmetatable(obj)
  9647. end
  9648. return setmetatable(t,klass)
  9649. end
  9650.  
  9651. local function is_list(t)
  9652. return getmetatable(t) == List
  9653. end
  9654.  
  9655. local function simple_table(t)
  9656. return type(t) == 'table' and not is_list(t) and #t > 0
  9657. end
  9658.  
  9659. function List:_init (src)
  9660. if src then
  9661. for v in iter(src) do
  9662. tinsert(self,v)
  9663. end
  9664. end
  9665. end
  9666.  
  9667. --- Create a new list. Can optionally pass a table;
  9668. -- passing another instance of List will cause a copy to be created
  9669. -- we pass anything which isn't a simple table to iterate() to work out
  9670. -- an appropriate iterator @see List.iterate
  9671. -- @param t An optional list-like table
  9672. -- @return a new List
  9673. -- @usage ls = List(); ls = List {1,2,3,4}
  9674. function List.new(t)
  9675. local ls
  9676. if not simple_table(t) then
  9677. ls = {}
  9678. List._init(ls,t)
  9679. else
  9680. ls = t
  9681. end
  9682. makelist(ls)
  9683. return ls
  9684. end
  9685.  
  9686. function List:clone()
  9687. local ls = makelist({},self)
  9688. List._init(ls,self)
  9689. return ls
  9690. end
  9691.  
  9692. function List.default_map_with(T)
  9693. return function(self,name)
  9694. local f = T[name]
  9695. if f then
  9696. return function(self,...)
  9697. return self:map(f,...)
  9698. end
  9699. else
  9700. error("method not found: "..name,2)
  9701. end
  9702. end
  9703. end
  9704.  
  9705.  
  9706. ---Add an item to the end of the list.
  9707. -- @param i An item
  9708. -- @return the list
  9709. function List:append(i)
  9710. tinsert(self,i)
  9711. return self
  9712. end
  9713.  
  9714. List.push = tinsert
  9715.  
  9716. --- Extend the list by appending all the items in the given list.
  9717. -- equivalent to 'a[len(a):] = L'.
  9718. -- @param L Another List
  9719. -- @return the list
  9720. function List:extend(L)
  9721. assert_arg(1,L,'table')
  9722. for i = 1,#L do tinsert(self,L[i]) end
  9723. return self
  9724. end
  9725.  
  9726. --- Insert an item at a given position. i is the index of the
  9727. -- element before which to insert.
  9728. -- @param i index of element before whichh to insert
  9729. -- @param x A data item
  9730. -- @return the list
  9731. function List:insert(i, x)
  9732. assert_arg(1,i,'number')
  9733. tinsert(self,i,x)
  9734. return self
  9735. end
  9736.  
  9737. --- Insert an item at the begining of the list.
  9738. -- @param x a data item
  9739. -- @return the list
  9740. function List:put (x)
  9741. return self:insert(1,x)
  9742. end
  9743.  
  9744. --- Remove an element given its index.
  9745. -- (equivalent of Python's del s[i])
  9746. -- @param i the index
  9747. -- @return the list
  9748. function List:remove (i)
  9749. assert_arg(1,i,'number')
  9750. tremove(self,i)
  9751. return self
  9752. end
  9753.  
  9754. --- Remove the first item from the list whose value is given.
  9755. -- (This is called 'remove' in Python; renamed to avoid confusion
  9756. -- with table.remove)
  9757. -- Return nil if there is no such item.
  9758. -- @param x A data value
  9759. -- @return the list
  9760. function List:remove_value(x)
  9761. for i=1,#self do
  9762. if self[i]==x then tremove(self,i) return self end
  9763. end
  9764. return self
  9765. end
  9766.  
  9767. --- Remove the item at the given position in the list, and return it.
  9768. -- If no index is specified, a:pop() returns the last item in the list.
  9769. -- The item is also removed from the list.
  9770. -- @param i An index
  9771. -- @return the item
  9772. function List:pop(i)
  9773. if not i then i = #self end
  9774. assert_arg(1,i,'number')
  9775. return tremove(self,i)
  9776. end
  9777.  
  9778. List.get = List.pop
  9779.  
  9780. --- Return the index in the list of the first item whose value is given.
  9781. -- Return nil if there is no such item.
  9782. -- @class function
  9783. -- @name List:index
  9784. -- @param x A data value
  9785. -- @param idx where to start search (default 1)
  9786. -- @return the index, or nil if not found.
  9787.  
  9788. local tfind = tablex.find
  9789. List.index = tfind
  9790.  
  9791. --- does this list contain the value?.
  9792. -- @param x A data value
  9793. -- @return true or false
  9794. function List:contains(x)
  9795. return tfind(self,x) and true or false
  9796. end
  9797.  
  9798. --- Return the number of times value appears in the list.
  9799. -- @param x A data value
  9800. -- @return number of times x appears
  9801. function List:count(x)
  9802. local cnt=0
  9803. for i=1,#self do
  9804. if self[i]==x then cnt=cnt+1 end
  9805. end
  9806. return cnt
  9807. end
  9808.  
  9809. --- Sort the items of the list, in place.
  9810. -- @param cmp an optional comparison function, default '<'
  9811. -- @return the list
  9812. function List:sort(cmp)
  9813. if cmp then cmp = function_arg(1,cmp) end
  9814. tsort(self,cmp)
  9815. return self
  9816. end
  9817.  
  9818. --- return a sorted copy of this list.
  9819. -- @param cmp an optional comparison function, default '<'
  9820. -- @return a new list
  9821. function List:sorted(cmp)
  9822. return List(self):sort(cmp)
  9823. end
  9824.  
  9825. --- Reverse the elements of the list, in place.
  9826. -- @return the list
  9827. function List:reverse()
  9828. local t = self
  9829. local n = #t
  9830. local n2 = n/2
  9831. for i = 1,n2 do
  9832. local k = n-i+1
  9833. t[i],t[k] = t[k],t[i]
  9834. end
  9835. return self
  9836. end
  9837.  
  9838. --- return the minimum and the maximum value of the list.
  9839. -- @return minimum value
  9840. -- @return maximum value
  9841. function List:minmax()
  9842. local vmin,vmax = 1e70,-1e70
  9843. for i = 1,#self do
  9844. local v = self[i]
  9845. if v < vmin then vmin = v end
  9846. if v > vmax then vmax = v end
  9847. end
  9848. return vmin,vmax
  9849. end
  9850.  
  9851. --- Emulate list slicing. like 'list[first:last]' in Python.
  9852. -- If first or last are negative then they are relative to the end of the list
  9853. -- eg. slice(-2) gives last 2 entries in a list, and
  9854. -- slice(-4,-2) gives from -4th to -2nd
  9855. -- @param first An index
  9856. -- @param last An index
  9857. -- @return a new List
  9858. function List:slice(first,last)
  9859. return tsub(self,first,last)
  9860. end
  9861.  
  9862. --- empty the list.
  9863. -- @return the list
  9864. function List:clear()
  9865. for i=1,#self do tremove(self) end
  9866. return self
  9867. end
  9868.  
  9869. local eps = 1.0e-10
  9870.  
  9871. --- Emulate Python's range(x) function.
  9872. -- Include it in List table for tidiness
  9873. -- @param start A number
  9874. -- @param finish A number greater than start; if absent,
  9875. -- then start is 1 and finish is start
  9876. -- @param incr an optional increment (may be less than 1)
  9877. -- @return a List from start .. finish
  9878. -- @usage List.range(0,3) == List{0,1,2,3}
  9879. -- @usage List.range(4) = List{1,2,3,4}
  9880. -- @usage List.range(5,1,-1) == List{5,4,3,2,1}
  9881. function List.range(start,finish,incr)
  9882. if not finish then
  9883. finish = start
  9884. start = 1
  9885. end
  9886. if incr then
  9887. assert_arg(3,incr,'number')
  9888. if not utils.is_integer(incr) then finish = finish + eps end
  9889. else
  9890. incr = 1
  9891. end
  9892. assert_arg(1,start,'number')
  9893. assert_arg(2,finish,'number')
  9894. local t = List.new()
  9895. for i=start,finish,incr do tinsert(t,i) end
  9896. return t
  9897. end
  9898.  
  9899. --- list:len() is the same as #list.
  9900. function List:len()
  9901. return #self
  9902. end
  9903.  
  9904. -- Extended operations --
  9905.  
  9906. --- Remove a subrange of elements.
  9907. -- equivalent to 'del s[i1:i2]' in Python.
  9908. -- @param i1 start of range
  9909. -- @param i2 end of range
  9910. -- @return the list
  9911. function List:chop(i1,i2)
  9912. return tremovevalues(self,i1,i2)
  9913. end
  9914.  
  9915. --- Insert a sublist into a list
  9916. -- equivalent to 's[idx:idx] = list' in Python
  9917. -- @param idx index
  9918. -- @param list list to insert
  9919. -- @return the list
  9920. -- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20})
  9921. function List:splice(idx,list)
  9922. assert_arg(1,idx,'number')
  9923. idx = idx - 1
  9924. local i = 1
  9925. for v in iter(list) do
  9926. tinsert(self,i+idx,v)
  9927. i = i + 1
  9928. end
  9929. return self
  9930. end
  9931.  
  9932. --- general slice assignment s[i1:i2] = seq.
  9933. -- @param i1 start index
  9934. -- @param i2 end index
  9935. -- @param seq a list
  9936. -- @return the list
  9937. function List:slice_assign(i1,i2,seq)
  9938. assert_arg(1,i1,'number')
  9939. assert_arg(1,i2,'number')
  9940. i1,i2 = normalize_slice(self,i1,i2)
  9941. if i2 >= i1 then self:chop(i1,i2) end
  9942. self:splice(i1,seq)
  9943. return self
  9944. end
  9945.  
  9946. --- concatenation operator.
  9947. -- @param L another List
  9948. -- @return a new list consisting of the list with the elements of the new list appended
  9949. function List:__concat(L)
  9950. assert_arg(1,L,'table')
  9951. local ls = self:clone()
  9952. ls:extend(L)
  9953. return ls
  9954. end
  9955.  
  9956. --- equality operator ==. True iff all elements of two lists are equal.
  9957. -- @param L another List
  9958. -- @return true or false
  9959. function List:__eq(L)
  9960. if #self ~= #L then return false end
  9961. for i = 1,#self do
  9962. if self[i] ~= L[i] then return false end
  9963. end
  9964. return true
  9965. end
  9966.  
  9967. --- join the elements of a list using a delimiter. <br>
  9968. -- This method uses tostring on all elements.
  9969. -- @param delim a delimiter string, can be empty.
  9970. -- @return a string
  9971. function List:join (delim)
  9972. delim = delim or ''
  9973. assert_arg(1,delim,'string')
  9974. return concat(array_tostring(self),delim)
  9975. end
  9976.  
  9977. --- join a list of strings. <br>
  9978. -- Uses table.concat directly.
  9979. -- @class function
  9980. -- @name List:concat
  9981. -- @param delim a delimiter
  9982. -- @return a string
  9983. List.concat = concat
  9984.  
  9985. local function tostring_q(val)
  9986. local s = tostring(val)
  9987. if type(val) == 'string' then
  9988. s = '"'..s..'"'
  9989. end
  9990. return s
  9991. end
  9992.  
  9993. --- how our list should be rendered as a string. Uses join().
  9994. -- @see List:join
  9995. function List:__tostring()
  9996. return '{'..self:join(',',tostring_q)..'}'
  9997. end
  9998.  
  9999. --[[
  10000. -- NOTE: this works, but is unreliable. If you leave the loop before finishing,
  10001. -- then the iterator is not reset.
  10002. --- can iterate over a list directly.
  10003. -- @usage for v in ls do print(v) end
  10004. function List:__call()
  10005. if not self.key then self.key = 1 end
  10006. local value = self[self.key]
  10007. self.key = self.key + 1
  10008. if not value then self.key = nil end
  10009. return value
  10010. end
  10011. --]]
  10012.  
  10013. --[[
  10014. function List.__call(t,v,i)
  10015. i = (i or 0) + 1
  10016. v = t[i]
  10017. if v then return i, v end
  10018. end
  10019. --]]
  10020.  
  10021. local MethodIter = {}
  10022.  
  10023. function MethodIter:__index (name)
  10024. return function(mm,...)
  10025. return self.list:foreachm(name,...)
  10026. end
  10027. end
  10028.  
  10029. --- call the function for each element of the list.
  10030. -- @param fun a function or callable object
  10031. -- @param ... optional values to pass to function
  10032. function List:foreach (fun,...)
  10033. if fun==nil then
  10034. return setmetatable({list=self},MethodIter)
  10035. end
  10036. fun = function_arg(1,fun)
  10037. for i = 1,#self do
  10038. fun(self[i],...)
  10039. end
  10040. end
  10041.  
  10042. function List:foreachm (name,...)
  10043. for i = 1,#self do
  10044. local obj = self[i]
  10045. local f = assert(obj[name],"method not found on object")
  10046. f(obj,...)
  10047. end
  10048. end
  10049.  
  10050. --- create a list of all elements which match a function.
  10051. -- @param fun a boolean function
  10052. -- @param arg optional argument to be passed as second argument of the predicate
  10053. -- @return a new filtered list.
  10054. function List:filter (fun,arg)
  10055. return makelist(filter(self,fun,arg),self)
  10056. end
  10057.  
  10058. --- split a string using a delimiter.
  10059. -- @param s the string
  10060. -- @param delim the delimiter (default spaces)
  10061. -- @return a List of strings
  10062. -- @see pl.utils.split
  10063. function List.split (s,delim)
  10064. assert_arg(1,s,'string')
  10065. return makelist(split(s,delim))
  10066. end
  10067.  
  10068. local MethodMapper = {}
  10069.  
  10070. function MethodMapper:__index (name)
  10071. return function(mm,...)
  10072. return self.list:mapm(name,...)
  10073. end
  10074. end
  10075.  
  10076. --- apply a function to all elements.
  10077. -- Any extra arguments will be passed to the function; if the function
  10078. -- is `nil` then `map` returns a mapper object that maps over a method
  10079. -- of the items
  10080. -- @param fun a function of at least one argument
  10081. -- @param ... arbitrary extra arguments.
  10082. -- @return a new list: {f(x) for x in self}
  10083. -- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'}
  10084. -- @usage List{'one','two'}:map():sub(1,2) == {'on','tw'}
  10085. -- @see pl.tablex.imap
  10086. function List:map (fun,...)
  10087. if fun==nil then
  10088. return setmetatable({list=self},MethodMapper)
  10089. end
  10090. return makelist(imap(fun,self,...),self)
  10091. end
  10092.  
  10093. --- apply a function to all elements, in-place.
  10094. -- Any extra arguments are passed to the function.
  10095. -- @param fun A function that takes at least one argument
  10096. -- @param ... arbitrary extra arguments.
  10097. -- @return the list.
  10098. function List:transform (fun,...)
  10099. transform(fun,self,...)
  10100. return self
  10101. end
  10102.  
  10103. --- apply a function to elements of two lists.
  10104. -- Any extra arguments will be passed to the function
  10105. -- @param fun a function of at least two arguments
  10106. -- @param ls another list
  10107. -- @param ... arbitrary extra arguments.
  10108. -- @return a new list: {f(x,y) for x in self, for x in arg1}
  10109. -- @see pl.tablex.imap2
  10110. function List:map2 (fun,ls,...)
  10111. return makelist(imap2(fun,self,ls,...),self)
  10112. end
  10113.  
  10114. --- apply a named method to all elements.
  10115. -- Any extra arguments will be passed to the method.
  10116. -- @param name name of method
  10117. -- @param ... extra arguments
  10118. -- @return a new list of the results
  10119. -- @see pl.seq.mapmethod
  10120. function List:mapm (name,...)
  10121. local res = {}
  10122. local t = self
  10123. for i = 1,#t do
  10124. local val = t[i]
  10125. local fn = val[name]
  10126. if not fn then error(type(val).." does not have method "..name,2) end
  10127. res[i] = fn(val,...)
  10128. end
  10129. return makelist(res,self)
  10130. end
  10131.  
  10132. --- 'reduce' a list using a binary function.
  10133. -- @param fun a function of two arguments
  10134. -- @return result of the function
  10135. -- @see pl.tablex.reduce
  10136. function List:reduce (fun)
  10137. return reduce(fun,self)
  10138. end
  10139.  
  10140. --- partition a list using a classifier function.
  10141. -- The function may return nil, but this will be converted to the string key '<nil>'.
  10142. -- @param fun a function of at least one argument
  10143. -- @param ... will also be passed to the function
  10144. -- @return a table where the keys are the returned values, and the values are Lists
  10145. -- of values where the function returned that key. It is given the type of Multimap.
  10146. -- @see pl.MultiMap
  10147. function List:partition (fun,...)
  10148. fun = function_arg(1,fun)
  10149. local res = {}
  10150. for i = 1,#self do
  10151. local val = self[i]
  10152. local klass = fun(val,...)
  10153. if klass == nil then klass = '<nil>' end
  10154. if not res[klass] then res[klass] = List() end
  10155. res[klass]:append(val)
  10156. end
  10157. return setmetatable(res,Multimap)
  10158. end
  10159.  
  10160. --- return an iterator over all values.
  10161. function List:iter ()
  10162. return iter(self)
  10163. end
  10164.  
  10165. --- Create an iterator over a seqence.
  10166. -- This captures the Python concept of 'sequence'.
  10167. -- For tables, iterates over all values with integer indices.
  10168. -- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function
  10169. -- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55
  10170. -- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p
  10171. function List.iterate(seq)
  10172. if type(seq) == 'string' then
  10173. local idx = 0
  10174. local n = #seq
  10175. local sub = string.sub
  10176. return function ()
  10177. idx = idx + 1
  10178. if idx > n then return nil
  10179. else
  10180. return sub(seq,idx,idx)
  10181. end
  10182. end
  10183. elseif type(seq) == 'table' then
  10184. local idx = 0
  10185. local n = #seq
  10186. return function()
  10187. idx = idx + 1
  10188. if idx > n then return nil
  10189. else
  10190. return seq[idx]
  10191. end
  10192. end
  10193. elseif type(seq) == 'function' then
  10194. return seq
  10195. elseif type(seq) == 'userdata' and io.type(seq) == 'file' then
  10196. return seq:lines()
  10197. end
  10198. end
  10199. iter = List.iterate
  10200.  
  10201. return List
  10202.  
  10203. ]===================],"CoolisOS/bin/utils/pl/tablex.lua",[===================[--- Extended operations on Lua tables.
  10204. --
  10205. -- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide}
  10206. --
  10207. -- Dependencies: `pl.utils`
  10208. -- @module pl.tablex
  10209. local utils = require'utils'
  10210. local getmetatable,setmetatable,require = getmetatable,setmetatable,require
  10211. local append,remove = table.insert,table.remove
  10212. local min,max = math.min,math.max
  10213. local pairs,type,unpack,next,select,tostring = pairs,type,unpack,next,select,tostring
  10214. local function_arg = utils.function_arg
  10215. local Set = utils.stdmt.Set
  10216. local List = utils.stdmt.List
  10217. local Map = utils.stdmt.Map
  10218. local assert_arg = utils.assert_arg
  10219.  
  10220. local tablex = {}
  10221.  
  10222. -- generally, functions that make copies of tables try to preserve the metatable.
  10223. -- However, when the source has no obvious type, then we attach appropriate metatables
  10224. -- like List, Map, etc to the result.
  10225. local function setmeta (res,tbl,def)
  10226. local mt = getmetatable(tbl) or def
  10227. return setmetatable(res, mt)
  10228. end
  10229.  
  10230. local function makelist (res)
  10231. return setmetatable(res,List)
  10232. end
  10233.  
  10234. local function check_meta (val)
  10235. if type(val) == 'table' then return true end
  10236. return getmetatable(val)
  10237. end
  10238.  
  10239. local function complain (idx,msg)
  10240. error(('argument %d is not %s'):format(idx,msg),3)
  10241. end
  10242.  
  10243. local function assert_arg_indexable (idx,val)
  10244. local mt = check_meta(val)
  10245. if mt == true then return end
  10246. if not(mt and mt.__len and mt.__index) then
  10247. complain(idx,"indexable")
  10248. end
  10249. end
  10250.  
  10251. local function assert_arg_iterable (idx,val)
  10252. local mt = check_meta(val)
  10253. if mt == true then return end
  10254. if not(mt and mt.__pairs) then
  10255. complain(idx,"iterable")
  10256. end
  10257. end
  10258.  
  10259. local function assert_arg_writeable (idx,val)
  10260. local mt = check_meta(val)
  10261. if mt == true then return end
  10262. if not(mt and mt.__newindex) then
  10263. complain(idx,"writeable")
  10264. end
  10265. end
  10266.  
  10267.  
  10268. --- copy a table into another, in-place.
  10269. -- @param t1 destination table
  10270. -- @param t2 source (any iterable object)
  10271. -- @return first table
  10272. function tablex.update (t1,t2)
  10273. assert_arg_writeable(1,t1)
  10274. assert_arg_iterable(2,t2)
  10275. for k,v in pairs(t2) do
  10276. t1[k] = v
  10277. end
  10278. return t1
  10279. end
  10280.  
  10281. --- total number of elements in this table.
  10282. -- Note that this is distinct from `#t`, which is the number
  10283. -- of values in the array part; this value will always
  10284. -- be greater or equal. The difference gives the size of
  10285. -- the hash part, for practical purposes. Works for any
  10286. -- object with a __pairs metamethod.
  10287. -- @param t a table
  10288. -- @return the size
  10289. function tablex.size (t)
  10290. assert_arg_iterable(1,t)
  10291. local i = 0
  10292. for k in pairs(t) do i = i + 1 end
  10293. return i
  10294. end
  10295.  
  10296. --- make a shallow copy of a table
  10297. -- @param t an iterable source
  10298. -- @return new table
  10299. function tablex.copy (t)
  10300. assert_arg_iterable(1,t)
  10301. local res = {}
  10302. for k,v in pairs(t) do
  10303. res[k] = v
  10304. end
  10305. return res
  10306. end
  10307.  
  10308. --- make a deep copy of a table, recursively copying all the keys and fields.
  10309. -- This will also set the copied table's metatable to that of the original.
  10310. -- @param t A table
  10311. -- @return new table
  10312. function tablex.deepcopy(t)
  10313. if type(t) ~= 'table' then return t end
  10314. assert_arg_iterable(1,t)
  10315. local mt = getmetatable(t)
  10316. local res = {}
  10317. for k,v in pairs(t) do
  10318. if type(v) == 'table' then
  10319. v = tablex.deepcopy(v)
  10320. end
  10321. res[k] = v
  10322. end
  10323. setmetatable(res,mt)
  10324. return res
  10325. end
  10326.  
  10327. local abs, deepcompare = math.abs
  10328.  
  10329. --- compare two values.
  10330. -- if they are tables, then compare their keys and fields recursively.
  10331. -- @param t1 A value
  10332. -- @param t2 A value
  10333. -- @param ignore_mt if true, ignore __eq metamethod (default false)
  10334. -- @param eps if defined, then used for any number comparisons
  10335. -- @return true or false
  10336. function tablex.deepcompare(t1,t2,ignore_mt,eps)
  10337. local ty1 = type(t1)
  10338. local ty2 = type(t2)
  10339. if ty1 ~= ty2 then return false end
  10340. -- non-table types can be directly compared
  10341. if ty1 ~= 'table' then
  10342. if ty1 == 'number' and eps then return abs(t1-t2) < eps end
  10343. return t1 == t2
  10344. end
  10345. -- as well as tables which have the metamethod __eq
  10346. local mt = getmetatable(t1)
  10347. if not ignore_mt and mt and mt.__eq then return t1 == t2 end
  10348. for k1 in pairs(t1) do
  10349. if t2[k1]==nil then return false end
  10350. end
  10351. for k2 in pairs(t2) do
  10352. if t1[k2]==nil then return false end
  10353. end
  10354. for k1,v1 in pairs(t1) do
  10355. local v2 = t2[k1]
  10356. if not deepcompare(v1,v2,ignore_mt,eps) then return false end
  10357. end
  10358.  
  10359. return true
  10360. end
  10361.  
  10362. deepcompare = tablex.deepcompare
  10363.  
  10364. --- compare two arrays using a predicate.
  10365. -- @param t1 an array
  10366. -- @param t2 an array
  10367. -- @param cmp A comparison function
  10368. function tablex.compare (t1,t2,cmp)
  10369. assert_arg_indexable(1,t1)
  10370. assert_arg_indexable(2,t2)
  10371. if #t1 ~= #t2 then return false end
  10372. cmp = function_arg(3,cmp)
  10373. for k = 1,#t1 do
  10374. if not cmp(t1[k],t2[k]) then return false end
  10375. end
  10376. return true
  10377. end
  10378.  
  10379. --- compare two list-like tables using an optional predicate, without regard for element order.
  10380. -- @param t1 a list-like table
  10381. -- @param t2 a list-like table
  10382. -- @param cmp A comparison function (may be nil)
  10383. function tablex.compare_no_order (t1,t2,cmp)
  10384. assert_arg_indexable(1,t1)
  10385. assert_arg_indexable(2,t2)
  10386. if cmp then cmp = function_arg(3,cmp) end
  10387. if #t1 ~= #t2 then return false end
  10388. local visited = {}
  10389. for i = 1,#t1 do
  10390. local val = t1[i]
  10391. local gotcha
  10392. for j = 1,#t2 do if not visited[j] then
  10393. local match
  10394. if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end
  10395. if match then
  10396. gotcha = j
  10397. break
  10398. end
  10399. end end
  10400. if not gotcha then return false end
  10401. visited[gotcha] = true
  10402. end
  10403. return true
  10404. end
  10405.  
  10406.  
  10407. --- return the index of a value in a list.
  10408. -- Like string.find, there is an optional index to start searching,
  10409. -- which can be negative.
  10410. -- @param t A list-like table (i.e. with numerical indices)
  10411. -- @param val A value
  10412. -- @param idx index to start; -1 means last element,etc (default 1)
  10413. -- @return index of value or nil if not found
  10414. -- @usage find({10,20,30},20) == 2
  10415. -- @usage find({'a','b','a','c'},'a',2) == 3
  10416. function tablex.find(t,val,idx)
  10417. assert_arg_indexable(1,t)
  10418. idx = idx or 1
  10419. if idx < 0 then idx = #t + idx + 1 end
  10420. for i = idx,#t do
  10421. if t[i] == val then return i end
  10422. end
  10423. return nil
  10424. end
  10425.  
  10426. --- return the index of a value in a list, searching from the end.
  10427. -- Like string.find, there is an optional index to start searching,
  10428. -- which can be negative.
  10429. -- @param t A list-like table (i.e. with numerical indices)
  10430. -- @param val A value
  10431. -- @param idx index to start; -1 means last element,etc (default 1)
  10432. -- @return index of value or nil if not found
  10433. -- @usage rfind({10,10,10},10) == 3
  10434. function tablex.rfind(t,val,idx)
  10435. assert_arg_indexable(1,t)
  10436. idx = idx or #t
  10437. if idx < 0 then idx = #t + idx + 1 end
  10438. for i = idx,1,-1 do
  10439. if t[i] == val then return i end
  10440. end
  10441. return nil
  10442. end
  10443.  
  10444.  
  10445. --- return the index (or key) of a value in a table using a comparison function.
  10446. -- @param t A table
  10447. -- @param cmp A comparison function
  10448. -- @param arg an optional second argument to the function
  10449. -- @return index of value, or nil if not found
  10450. -- @return value returned by comparison function
  10451. function tablex.find_if(t,cmp,arg)
  10452. assert_arg_iterable(1,t)
  10453. cmp = function_arg(2,cmp)
  10454. for k,v in pairs(t) do
  10455. local c = cmp(v,arg)
  10456. if c then return k,c end
  10457. end
  10458. return nil
  10459. end
  10460.  
  10461. --- return a list of all values in a table indexed by another list.
  10462. -- @param tbl a table
  10463. -- @param idx an index table (a list of keys)
  10464. -- @return a list-like table
  10465. -- @usage index_by({10,20,30,40},{2,4}) == {20,40}
  10466. -- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3}
  10467. function tablex.index_by(tbl,idx)
  10468. assert_arg_indexable(1,tbl)
  10469. assert_arg_indexable(2,idx)
  10470. local res = {}
  10471. for i = 1,#idx do
  10472. res[i] = tbl[idx[i]]
  10473. end
  10474. return setmeta(res,tbl,List)
  10475. end
  10476.  
  10477. --- apply a function to all values of a table.
  10478. -- This returns a table of the results.
  10479. -- Any extra arguments are passed to the function.
  10480. -- @param fun A function that takes at least one argument
  10481. -- @param t A table
  10482. -- @param ... optional arguments
  10483. -- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4}
  10484. function tablex.map(fun,t,...)
  10485. assert_arg_iterable(1,t)
  10486. fun = function_arg(1,fun)
  10487. local res = {}
  10488. for k,v in pairs(t) do
  10489. res[k] = fun(v,...)
  10490. end
  10491. return setmeta(res,t)
  10492. end
  10493.  
  10494. --- apply a function to all values of a list.
  10495. -- This returns a table of the results.
  10496. -- Any extra arguments are passed to the function.
  10497. -- @param fun A function that takes at least one argument
  10498. -- @param t a table (applies to array part)
  10499. -- @param ... optional arguments
  10500. -- @return a list-like table
  10501. -- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900}
  10502. function tablex.imap(fun,t,...)
  10503. assert_arg_indexable(1,t)
  10504. fun = function_arg(1,fun)
  10505. local res = {}
  10506. for i = 1,#t do
  10507. res[i] = fun(t[i],...) or false
  10508. end
  10509. return setmeta(res,t,List)
  10510. end
  10511.  
  10512. --- apply a named method to values from a table.
  10513. -- @param name the method name
  10514. -- @param t a list-like table
  10515. -- @param ... any extra arguments to the method
  10516. function tablex.map_named_method (name,t,...)
  10517. utils.assert_string(1,name)
  10518. assert_arg_indexable(2,t)
  10519. local res = {}
  10520. for i = 1,#t do
  10521. local val = t[i]
  10522. local fun = val[name]
  10523. res[i] = fun(val,...)
  10524. end
  10525. return setmeta(res,t,List)
  10526. end
  10527.  
  10528.  
  10529. --- apply a function to all values of a table, in-place.
  10530. -- Any extra arguments are passed to the function.
  10531. -- @param fun A function that takes at least one argument
  10532. -- @param t a table
  10533. -- @param ... extra arguments
  10534. function tablex.transform (fun,t,...)
  10535. assert_arg_iterable(1,t)
  10536. fun = function_arg(1,fun)
  10537. for k,v in pairs(t) do
  10538. t[k] = fun(v,...)
  10539. end
  10540. end
  10541.  
  10542. --- generate a table of all numbers in a range
  10543. -- @param start number
  10544. -- @param finish number
  10545. -- @param step optional increment (default 1 for increasing, -1 for decreasing)
  10546. function tablex.range (start,finish,step)
  10547. if start == finish then return {start}
  10548. elseif start > finish then return {}
  10549. end
  10550. local res = {}
  10551. local k = 1
  10552. if not step then
  10553. if finish > start then step = finish > start and 1 or -1 end
  10554. end
  10555. for i=start,finish,step do res[k]=i; k=k+1 end
  10556. return res
  10557. end
  10558.  
  10559. --- apply a function to values from two tables.
  10560. -- @param fun a function of at least two arguments
  10561. -- @param t1 a table
  10562. -- @param t2 a table
  10563. -- @param ... extra arguments
  10564. -- @return a table
  10565. -- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44}
  10566. function tablex.map2 (fun,t1,t2,...)
  10567. assert_arg_iterable(1,t1)
  10568. assert_arg_iterable(2,t2)
  10569. fun = function_arg(1,fun)
  10570. local res = {}
  10571. for k,v in pairs(t1) do
  10572. res[k] = fun(v,t2[k],...)
  10573. end
  10574. return setmeta(res,t1,List)
  10575. end
  10576.  
  10577. --- apply a function to values from two arrays.
  10578. -- The result will be the length of the shortest array.
  10579. -- @param fun a function of at least two arguments
  10580. -- @param t1 a list-like table
  10581. -- @param t2 a list-like table
  10582. -- @param ... extra arguments
  10583. -- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23}
  10584. function tablex.imap2 (fun,t1,t2,...)
  10585. assert_arg_indexable(2,t1)
  10586. assert_arg_indexable(3,t2)
  10587. fun = function_arg(1,fun)
  10588. local res,n = {},math.min(#t1,#t2)
  10589. for i = 1,n do
  10590. res[i] = fun(t1[i],t2[i],...)
  10591. end
  10592. return res
  10593. end
  10594.  
  10595. --- 'reduce' a list using a binary function.
  10596. -- @param fun a function of two arguments
  10597. -- @param t a list-like table
  10598. -- @return the result of the function
  10599. -- @usage reduce('+',{1,2,3,4}) == 10
  10600. function tablex.reduce (fun,t)
  10601. assert_arg_indexable(2,t)
  10602. fun = function_arg(1,fun)
  10603. local n = #t
  10604. local res = t[1]
  10605. for i = 2,n do
  10606. res = fun(res,t[i])
  10607. end
  10608. return res
  10609. end
  10610.  
  10611. --- apply a function to all elements of a table.
  10612. -- The arguments to the function will be the value,
  10613. -- the key and <i>finally</i> any extra arguments passed to this function.
  10614. -- Note that the Lua 5.0 function table.foreach passed the <i>key</i> first.
  10615. -- @param t a table
  10616. -- @param fun a function with at least one argument
  10617. -- @param ... extra arguments
  10618. function tablex.foreach(t,fun,...)
  10619. assert_arg_iterable(1,t)
  10620. fun = function_arg(2,fun)
  10621. for k,v in pairs(t) do
  10622. fun(v,k,...)
  10623. end
  10624. end
  10625.  
  10626. --- apply a function to all elements of a list-like table in order.
  10627. -- The arguments to the function will be the value,
  10628. -- the index and <i>finally</i> any extra arguments passed to this function
  10629. -- @param t a table
  10630. -- @param fun a function with at least one argument
  10631. -- @param ... optional arguments
  10632. function tablex.foreachi(t,fun,...)
  10633. assert_arg_indexable(1,t)
  10634. fun = function_arg(2,fun)
  10635. for i = 1,#t do
  10636. fun(t[i],i,...)
  10637. end
  10638. end
  10639.  
  10640.  
  10641. --- Apply a function to a number of tables.
  10642. -- A more general version of map
  10643. -- The result is a table containing the result of applying that function to the
  10644. -- ith value of each table. Length of output list is the minimum length of all the lists
  10645. -- @param fun a function of n arguments
  10646. -- @param ... n tables
  10647. -- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333}
  10648. -- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300}
  10649. -- @param fun A function that takes as many arguments as there are tables
  10650. function tablex.mapn(fun,...)
  10651. fun = function_arg(1,fun)
  10652. local res = {}
  10653. local lists = {...}
  10654. local minn = 1e40
  10655. for i = 1,#lists do
  10656. minn = min(minn,#(lists[i]))
  10657. end
  10658. for i = 1,minn do
  10659. local args,k = {},1
  10660. for j = 1,#lists do
  10661. args[k] = lists[j][i]
  10662. k = k + 1
  10663. end
  10664. res[#res+1] = fun(unpack(args))
  10665. end
  10666. return res
  10667. end
  10668.  
  10669. --- call the function with the key and value pairs from a table.
  10670. -- The function can return a value and a key (note the order!). If both
  10671. -- are not nil, then this pair is inserted into the result. If only value is not nil, then
  10672. -- it is appended to the result.
  10673. -- @param fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap.
  10674. -- @param t A table
  10675. -- @param ... optional arguments
  10676. -- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10}
  10677. -- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}}
  10678. function tablex.pairmap(fun,t,...)
  10679. assert_arg_iterable(1,t)
  10680. fun = function_arg(1,fun)
  10681. local res = {}
  10682. for k,v in pairs(t) do
  10683. local rv,rk = fun(k,v,...)
  10684. if rk then
  10685. res[rk] = rv
  10686. else
  10687. res[#res+1] = rv
  10688. end
  10689. end
  10690. return res
  10691. end
  10692.  
  10693. local function keys_op(i,v) return i end
  10694.  
  10695. --- return all the keys of a table in arbitrary order.
  10696. -- @param t A table
  10697. function tablex.keys(t)
  10698. assert_arg_iterable(1,t)
  10699. return makelist(tablex.pairmap(keys_op,t))
  10700. end
  10701.  
  10702. local function values_op(i,v) return v end
  10703.  
  10704. --- return all the values of the table in arbitrary order
  10705. -- @param t A table
  10706. function tablex.values(t)
  10707. assert_arg_iterable(1,t)
  10708. return makelist(tablex.pairmap(values_op,t))
  10709. end
  10710.  
  10711. local function index_map_op (i,v) return i,v end
  10712.  
  10713. --- create an index map from a list-like table. The original values become keys,
  10714. -- and the associated values are the indices into the original list.
  10715. -- @param t a list-like table
  10716. -- @return a map-like table
  10717. function tablex.index_map (t)
  10718. assert_arg_indexable(1,t)
  10719. return setmetatable(tablex.pairmap(index_map_op,t),Map)
  10720. end
  10721.  
  10722. local function set_op(i,v) return true,v end
  10723.  
  10724. --- create a set from a list-like table. A set is a table where the original values
  10725. -- become keys, and the associated values are all true.
  10726. -- @param t a list-like table
  10727. -- @return a set (a map-like table)
  10728. function tablex.makeset (t)
  10729. assert_arg_indexable(1,t)
  10730. return setmetatable(tablex.pairmap(set_op,t),Set)
  10731. end
  10732.  
  10733.  
  10734. --- combine two tables, either as union or intersection. Corresponds to
  10735. -- set operations for sets () but more general. Not particularly
  10736. -- useful for list-like tables.
  10737. -- @param t1 a table
  10738. -- @param t2 a table
  10739. -- @param dup true for a union, false for an intersection.
  10740. -- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34}
  10741. -- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23}
  10742. -- @see tablex.index_map
  10743. function tablex.merge (t1,t2,dup)
  10744. assert_arg_iterable(1,t1)
  10745. assert_arg_iterable(2,t2)
  10746. local res = {}
  10747. for k,v in pairs(t1) do
  10748. if dup or t2[k] then res[k] = v end
  10749. end
  10750. if dup then
  10751. for k,v in pairs(t2) do
  10752. res[k] = v
  10753. end
  10754. end
  10755. return setmeta(res,t1,Map)
  10756. end
  10757.  
  10758. --- a new table which is the difference of two tables.
  10759. -- With sets (where the values are all true) this is set difference and
  10760. -- symmetric difference depending on the third parameter.
  10761. -- @param s1 a map-like table or set
  10762. -- @param s2 a map-like table or set
  10763. -- @param symm symmetric difference (default false)
  10764. -- @return a map-like table or set
  10765. function tablex.difference (s1,s2,symm)
  10766. assert_arg_iterable(1,s1)
  10767. assert_arg_iterable(2,s2)
  10768. local res = {}
  10769. for k,v in pairs(s1) do
  10770. if not s2[k] then res[k] = v end
  10771. end
  10772. if symm then
  10773. for k,v in pairs(s2) do
  10774. if not s1[k] then res[k] = v end
  10775. end
  10776. end
  10777. return setmeta(res,s1,Map)
  10778. end
  10779.  
  10780. --- A table where the key/values are the values and value counts of the table.
  10781. -- @param t a list-like table
  10782. -- @param cmp a function that defines equality (otherwise uses ==)
  10783. -- @return a map-like table
  10784. -- @see seq.count_map
  10785. function tablex.count_map (t,cmp)
  10786. assert_arg_indexable(1,t)
  10787. local res,mask = {},{}
  10788. cmp = function_arg(2,cmp)
  10789. local n = #t
  10790. for i = 1,#t do
  10791. local v = t[i]
  10792. if not mask[v] then
  10793. mask[v] = true
  10794. -- check this value against all other values
  10795. res[v] = 1 -- there's at least one instance
  10796. for j = i+1,n do
  10797. local w = t[j]
  10798. if cmp and cmp(v,w) or v == w then
  10799. res[v] = res[v] + 1
  10800. mask[w] = true
  10801. end
  10802. end
  10803. end
  10804. end
  10805. return setmetatable(res,Map)
  10806. end
  10807.  
  10808. --- filter a table's values using a predicate function
  10809. -- @param t a list-like table
  10810. -- @param pred a boolean function
  10811. -- @param arg optional argument to be passed as second argument of the predicate
  10812. function tablex.filter (t,pred,arg)
  10813. assert_arg_indexable(1,t)
  10814. pred = function_arg(2,pred)
  10815. local res,k = {},1
  10816. for i = 1,#t do
  10817. local v = t[i]
  10818. if pred(v,arg) then
  10819. res[k] = v
  10820. k = k + 1
  10821. end
  10822. end
  10823. return setmeta(res,t,List)
  10824. end
  10825.  
  10826. --- return a table where each element is a table of the ith values of an arbitrary
  10827. -- number of tables. It is equivalent to a matrix transpose.
  10828. -- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}}
  10829. function tablex.zip(...)
  10830. return tablex.mapn(function(...) return {...} end,...)
  10831. end
  10832.  
  10833. local _copy
  10834. function _copy (dest,src,idest,isrc,nsrc,clean_tail)
  10835. idest = idest or 1
  10836. isrc = isrc or 1
  10837. local iend
  10838. if not nsrc then
  10839. nsrc = #src
  10840. iend = #src
  10841. else
  10842. iend = isrc + min(nsrc-1,#src-isrc)
  10843. end
  10844. if dest == src then -- special case
  10845. if idest > isrc and iend >= idest then -- overlapping ranges
  10846. src = tablex.sub(src,isrc,nsrc)
  10847. isrc = 1; iend = #src
  10848. end
  10849. end
  10850. for i = isrc,iend do
  10851. dest[idest] = src[i]
  10852. idest = idest + 1
  10853. end
  10854. if clean_tail then
  10855. tablex.clear(dest,idest)
  10856. end
  10857. return dest
  10858. end
  10859.  
  10860. --- copy an array into another one, resizing the destination if necessary. <br>
  10861. -- @param dest a list-like table
  10862. -- @param src a list-like table
  10863. -- @param idest where to start copying values from source (default 1)
  10864. -- @param isrc where to start copying values into destination (default 1)
  10865. -- @param nsrc number of elements to copy from source (default source size)
  10866. function tablex.icopy (dest,src,idest,isrc,nsrc)
  10867. assert_arg_indexable(1,dest)
  10868. assert_arg_indexable(2,src)
  10869. return _copy(dest,src,idest,isrc,nsrc,true)
  10870. end
  10871.  
  10872. --- copy an array into another one. <br>
  10873. -- @param dest a list-like table
  10874. -- @param src a list-like table
  10875. -- @param idest where to start copying values from source (default 1)
  10876. -- @param isrc where to start copying values into destination (default 1)
  10877. -- @param nsrc number of elements to copy from source (default source size)
  10878. function tablex.move (dest,src,idest,isrc,nsrc)
  10879. assert_arg_indexable(1,dest)
  10880. assert_arg_indexable(2,src)
  10881. return _copy(dest,src,idest,isrc,nsrc,false)
  10882. end
  10883.  
  10884. function tablex._normalize_slice(self,first,last)
  10885. local sz = #self
  10886. if not first then first=1 end
  10887. if first<0 then first=sz+first+1 end
  10888. -- make the range _inclusive_!
  10889. if not last then last=sz end
  10890. if last < 0 then last=sz+1+last end
  10891. return first,last
  10892. end
  10893.  
  10894. --- Extract a range from a table, like 'string.sub'.
  10895. -- If first or last are negative then they are relative to the end of the list
  10896. -- eg. sub(t,-2) gives last 2 entries in a list, and
  10897. -- sub(t,-4,-2) gives from -4th to -2nd
  10898. -- @param t a list-like table
  10899. -- @param first An index
  10900. -- @param last An index
  10901. -- @return a new List
  10902. function tablex.sub(t,first,last)
  10903. assert_arg_indexable(1,t)
  10904. first,last = tablex._normalize_slice(t,first,last)
  10905. local res={}
  10906. for i=first,last do append(res,t[i]) end
  10907. return setmeta(res,t,List)
  10908. end
  10909.  
  10910. --- set an array range to a value. If it's a function we use the result
  10911. -- of applying it to the indices.
  10912. -- @param t a list-like table
  10913. -- @param val a value
  10914. -- @param i1 start range (default 1)
  10915. -- @param i2 end range (default table size)
  10916. function tablex.set (t,val,i1,i2)
  10917. assert_arg_indexable(1,t)
  10918. i1,i2 = i1 or 1,i2 or #t
  10919. if utils.is_callable(val) then
  10920. for i = i1,i2 do
  10921. t[i] = val(i)
  10922. end
  10923. else
  10924. for i = i1,i2 do
  10925. t[i] = val
  10926. end
  10927. end
  10928. end
  10929.  
  10930. --- create a new array of specified size with initial value.
  10931. -- @param n size
  10932. -- @param val initial value (can be nil, but don't expect # to work!)
  10933. -- @return the table
  10934. function tablex.new (n,val)
  10935. local res = {}
  10936. tablex.set(res,val,1,n)
  10937. return res
  10938. end
  10939.  
  10940. --- clear out the contents of a table.
  10941. -- @param t a table
  10942. -- @param istart optional start position
  10943. function tablex.clear(t,istart)
  10944. istart = istart or 1
  10945. for i = istart,#t do remove(t) end
  10946. end
  10947.  
  10948. --- insert values into a table. <br>
  10949. -- insertvalues(t, [pos,] values) <br>
  10950. -- similar to table.insert but inserts values from given table "values",
  10951. -- not the object itself, into table "t" at position "pos".
  10952. function tablex.insertvalues(t, ...)
  10953. assert_arg(1,t,'table')
  10954. local pos, values
  10955. if select('#', ...) == 1 then
  10956. pos,values = #t+1, ...
  10957. else
  10958. pos,values = ...
  10959. end
  10960. if #values > 0 then
  10961. for i=#t,pos,-1 do
  10962. t[i+#values] = t[i]
  10963. end
  10964. local offset = 1 - pos
  10965. for i=pos,pos+#values-1 do
  10966. t[i] = values[i + offset]
  10967. end
  10968. end
  10969. return t
  10970. end
  10971.  
  10972. --- remove a range of values from a table.
  10973. -- @param t a list-like table
  10974. -- @param i1 start index
  10975. -- @param i2 end index
  10976. -- @return the table
  10977. function tablex.removevalues (t,i1,i2)
  10978. assert_arg(1,t,'table')
  10979. i1,i2 = tablex._normalize_slice(t,i1,i2)
  10980. for i = i1,i2 do
  10981. remove(t,i1)
  10982. end
  10983. return t
  10984. end
  10985.  
  10986. local _find
  10987. _find = function (t,value,tables)
  10988. for k,v in pairs(t) do
  10989. if v == value then return k end
  10990. end
  10991. for k,v in pairs(t) do
  10992. if not tables[v] and type(v) == 'table' then
  10993. tables[v] = true
  10994. local res = _find(v,value,tables)
  10995. if res then
  10996. res = tostring(res)
  10997. if type(k) ~= 'string' then
  10998. return '['..k..']'..res
  10999. else
  11000. return k..'.'..res
  11001. end
  11002. end
  11003. end
  11004. end
  11005. end
  11006.  
  11007. --- find a value in a table by recursive search.
  11008. -- @param t the table
  11009. -- @param value the value
  11010. -- @param exclude any tables to avoid searching
  11011. -- @usage search(_G,math.sin,{package.path}) == 'math.sin'
  11012. -- @return a fieldspec, e.g. 'a.b' or 'math.sin'
  11013. function tablex.search (t,value,exclude)
  11014. assert_arg_iterable(1,t)
  11015. local tables = {[t]=true}
  11016. if exclude then
  11017. for _,v in pairs(exclude) do tables[v] = true end
  11018. end
  11019. return _find(t,value,tables)
  11020. end
  11021.  
  11022. return tablex
  11023. ]===================],"CoolisOS/bin/utils/pl/utils.lua",[===================[--- Generally useful routines.
  11024. -- See @{01-introduction.md.Generally_useful_functions|the Guide}.
  11025. -- @module pl.utils
  11026. local format,gsub,byte = string.format,string.gsub,string.byte
  11027. local clock = os.clock
  11028. local stdout = io.stdout
  11029. local append = table.insert
  11030.  
  11031. local collisions = {}
  11032.  
  11033. local utils = {}
  11034.  
  11035. utils._VERSION = "1.1.0"
  11036.  
  11037. local lua51 = rawget(_G,'setfenv')
  11038.  
  11039. utils.lua51 = lua51
  11040. if not lua51 then -- Lua 5.2 compatibility
  11041. unpack = table.unpack
  11042. loadstring = load
  11043. end
  11044.  
  11045. utils.dir_separator = '/'
  11046.  
  11047. --- end this program gracefully.
  11048. -- @param code The exit code or a message to be printed
  11049. -- @param ... extra arguments for message's format'
  11050. -- @see utils.fprintf
  11051. function utils.quit(code,...)
  11052. if type(code) == 'string' then
  11053. utils.fprintf(io.stderr,code,...)
  11054. code = -1
  11055. else
  11056. utils.fprintf(io.stderr,...)
  11057. end
  11058. io.stderr:write('\n')
  11059. os.exit(code)
  11060. end
  11061.  
  11062. --- print an arbitrary number of arguments using a format.
  11063. -- @param fmt The format (see string.format)
  11064. -- @param ... Extra arguments for format
  11065. function utils.printf(fmt,...)
  11066. utils.assert_string(1,fmt)
  11067. utils.fprintf(stdout,fmt,...)
  11068. end
  11069.  
  11070. --- write an arbitrary number of arguments to a file using a format.
  11071. -- @param f File handle to write to.
  11072. -- @param fmt The format (see string.format).
  11073. -- @param ... Extra arguments for format
  11074. function utils.fprintf(f,fmt,...)
  11075. utils.assert_string(2,fmt)
  11076. f:write(format(fmt,...))
  11077. end
  11078.  
  11079. local function import_symbol(T,k,v,libname)
  11080. local key = rawget(T,k)
  11081. -- warn about collisions!
  11082. if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then
  11083. utils.printf("warning: '%s.%s' overrides existing symbol\n",libname,k)
  11084. end
  11085. rawset(T,k,v)
  11086. end
  11087.  
  11088. local function lookup_lib(T,t)
  11089. for k,v in pairs(T) do
  11090. if v == t then return k end
  11091. end
  11092. return '?'
  11093. end
  11094.  
  11095. local already_imported = {}
  11096.  
  11097. --- take a table and 'inject' it into the local namespace.
  11098. -- @param t The Table
  11099. -- @param T An optional destination table (defaults to callers environment)
  11100. function utils.import(t,T)
  11101. T = T or _G
  11102. t = t or utils
  11103. if type(t) == 'string' then
  11104. t = require (t)
  11105. end
  11106. local libname = lookup_lib(T,t)
  11107. if already_imported[t] then return end
  11108. already_imported[t] = libname
  11109. for k,v in pairs(t) do
  11110. import_symbol(T,k,v,libname)
  11111. end
  11112. end
  11113.  
  11114. utils.patterns = {
  11115. FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*',
  11116. INTEGER = '[+%-%d]%d*',
  11117. IDEN = '[%a_][%w_]*',
  11118. FILE = '[%a%.\\][:%][%w%._%-\\]*'
  11119. }
  11120.  
  11121. --- escape any 'magic' characters in a string
  11122. -- @param s The input string
  11123. function utils.escape(s)
  11124. utils.assert_string(1,s)
  11125. return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
  11126. end
  11127.  
  11128. --- return either of two values, depending on a condition.
  11129. -- @param cond A condition
  11130. -- @param value1 Value returned if cond is true
  11131. -- @param value2 Value returned if cond is false (can be optional)
  11132. function utils.choose(cond,value1,value2)
  11133. if cond then return value1
  11134. else return value2
  11135. end
  11136. end
  11137.  
  11138. local raise
  11139.  
  11140. --- return the contents of a file as a string
  11141. -- @param filename The file path
  11142. -- @param is_bin open in binary mode
  11143. -- @return file contents
  11144. function utils.readfile(filename,is_bin)
  11145. local mode = is_bin and 'b' or ''
  11146. utils.assert_string(1,filename)
  11147. local f,err = io.open(filename,'r'..mode)
  11148. if not f then return utils.raise (err) end
  11149. local res,err = f:read('*a')
  11150. f:close()
  11151. if not res then return raise (err) end
  11152. return res
  11153. end
  11154.  
  11155. --- write a string to a file
  11156. -- @param filename The file path
  11157. -- @param str The string
  11158. -- @return true or nil
  11159. -- @return error message
  11160. -- @raise error if filename or str aren't strings
  11161. function utils.writefile(filename,str)
  11162. utils.assert_string(1,filename)
  11163. utils.assert_string(2,str)
  11164. local f,err = io.open(filename,'w')
  11165. if not f then return raise(err) end
  11166. f:write(str)
  11167. f:close()
  11168. return true
  11169. end
  11170.  
  11171. --- return the contents of a file as a list of lines
  11172. -- @param filename The file path
  11173. -- @return file contents as a table
  11174. -- @raise errror if filename is not a string
  11175. function utils.readlines(filename)
  11176. utils.assert_string(1,filename)
  11177. local f,err = io.open(filename,'r')
  11178. if not f then return raise(err) end
  11179. local res = {}
  11180. for line in f:lines() do
  11181. append(res,line)
  11182. end
  11183. f:close()
  11184. return res
  11185. end
  11186.  
  11187. --- split a string into a list of strings separated by a delimiter.
  11188. -- @param s The input string
  11189. -- @param re A Lua string pattern; defaults to '%s+'
  11190. -- @param plain don't use Lua patterns
  11191. -- @param n optional maximum number of splits
  11192. -- @return a list-like table
  11193. -- @raise error if s is not a string
  11194. function utils.split(s,re,plain,n)
  11195. utils.assert_string(1,s)
  11196. local find,sub,append = string.find, string.sub, table.insert
  11197. local i1,ls = 1,{}
  11198. if not re then re = '%s+' end
  11199. if re == '' then return {s} end
  11200. while true do
  11201. local i2,i3 = find(s,re,i1,plain)
  11202. if not i2 then
  11203. local last = sub(s,i1)
  11204. if last ~= '' then append(ls,last) end
  11205. if #ls == 1 and ls[1] == '' then
  11206. return {}
  11207. else
  11208. return ls
  11209. end
  11210. end
  11211. append(ls,sub(s,i1,i2-1))
  11212. if n and #ls == n then
  11213. ls[#ls] = sub(s,i1)
  11214. return ls
  11215. end
  11216. i1 = i3+1
  11217. end
  11218. end
  11219.  
  11220. --- split a string into a number of values.
  11221. -- @param s the string
  11222. -- @param re the delimiter, default space
  11223. -- @return n values
  11224. -- @usage first,next = splitv('jane:doe',':')
  11225. -- @see split
  11226. function utils.splitv (s,re)
  11227. return unpack(utils.split(s,re))
  11228. end
  11229.  
  11230. --- convert an array of values to strings.
  11231. -- @param t a list-like table
  11232. -- @param temp buffer to use, otherwise allocate
  11233. -- @param tostr custom tostring function, called with (value,index).
  11234. -- Otherwise use `tostring`
  11235. -- @return the converted buffer
  11236. function utils.array_tostring (t,temp,tostr)
  11237. temp, tostr = temp or {}, tostr or tostring
  11238. for i = 1,#t do
  11239. temp[i] = tostr(t[i],i)
  11240. end
  11241. return temp
  11242. end
  11243.  
  11244. local lua51_load = load
  11245.  
  11246. if utils.lua51 then -- define Lua 5.2 style load()
  11247. function utils.load(str,src,mode,env)
  11248. local chunk,err
  11249. if type(str) == 'string' then
  11250. chunk,err = loadstring(str,src)
  11251. else
  11252. chunk,err = lua51_load(str,src)
  11253. end
  11254. if chunk and env then setfenv(chunk,env) end
  11255. return chunk,err
  11256. end
  11257. end
  11258.  
  11259.  
  11260. --- execute a shell command.
  11261. -- This is a compatibility function that returns the same for Lua 5.1 and Lua 5.2
  11262. -- @param cmd a shell command
  11263. -- @return true if successful
  11264. -- @return actual return code
  11265. function utils.execute (cmd)
  11266. local res1,res2,res2 = os.execute(cmd)
  11267. if lua51 then
  11268. return res1==0,res1
  11269. else
  11270. return res1,res2
  11271. end
  11272. end
  11273.  
  11274. if lua51 then
  11275. function table.pack (...)
  11276. local n = select('#',...)
  11277. return {n=n; ...}
  11278. end
  11279. local sep = '/'
  11280. function utils.searchpath (mod,path)
  11281. mod = mod:gsub('%.',sep)
  11282. for m in path:gmatch('[^;]+') do
  11283. local nm = m:gsub('?',mod)
  11284. local f = io.open(nm,'r')
  11285. if f then f:close(); return nm end
  11286. end
  11287. end
  11288. end
  11289.  
  11290. if not table.pack then table.pack = _G.pack end
  11291. if not rawget(_G,"pack") then _G.pack = table.pack end
  11292.  
  11293. --- take an arbitrary set of arguments and make into a table.
  11294. -- This returns the table and the size; works fine for nil arguments
  11295. -- @param ... arguments
  11296. -- @return table
  11297. -- @return table size
  11298. -- @usage local t,n = utils.args(...)
  11299.  
  11300. --- 'memoize' a function (cache returned value for next call).
  11301. -- This is useful if you have a function which is relatively expensive,
  11302. -- but you don't know in advance what values will be required, so
  11303. -- building a table upfront is wasteful/impossible.
  11304. -- @param func a function of at least one argument
  11305. -- @return a function with at least one argument, which is used as the key.
  11306. function utils.memoize(func)
  11307. return setmetatable({}, {
  11308. __index = function(self, k, ...)
  11309. local v = func(k,...)
  11310. self[k] = v
  11311. return v
  11312. end,
  11313. __call = function(self, k) return self[k] end
  11314. })
  11315. end
  11316.  
  11317. --- is the object either a function or a callable object?.
  11318. -- @param obj Object to check.
  11319. function utils.is_callable (obj)
  11320. return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call
  11321. end
  11322.  
  11323. --- is the object of the specified type?.
  11324. -- If the type is a string, then use type, otherwise compare with metatable
  11325. -- @param obj An object to check
  11326. -- @param tp String of what type it should be
  11327. function utils.is_type (obj,tp)
  11328. if type(tp) == 'string' then return type(obj) == tp end
  11329. local mt = getmetatable(obj)
  11330. return tp == mt
  11331. end
  11332.  
  11333. local fileMT = getmetatable(io.stdout)
  11334.  
  11335. --- a string representation of a type.
  11336. -- For tables with metatables, we assume that the metatable has a `_name`
  11337. -- field. Knows about Lua file objects.
  11338. -- @param obj an object
  11339. -- @return a string like 'number', 'table' or 'List'
  11340. function utils.type (obj)
  11341. local t = type(obj)
  11342. if t == 'table' or t == 'userdata' then
  11343. local mt = getmetatable(obj)
  11344. if mt == fileMT then
  11345. return 'file'
  11346. else
  11347. return mt._name or "unknown "..t
  11348. end
  11349. else
  11350. return t
  11351. end
  11352. end
  11353.  
  11354. --- is this number an integer?
  11355. -- @param x a number
  11356. -- @raise error if x is not a number
  11357. function utils.is_integer (x)
  11358. return math.ceil(x)==x
  11359. end
  11360.  
  11361. utils.stdmt = {
  11362. List = {_name='List'}, Map = {_name='Map'},
  11363. Set = {_name='Set'}, MultiMap = {_name='MultiMap'}
  11364. }
  11365.  
  11366. local _function_factories = {}
  11367.  
  11368. --- associate a function factory with a type.
  11369. -- A function factory takes an object of the given type and
  11370. -- returns a function for evaluating it
  11371. -- @param mt metatable
  11372. -- @param fun a callable that returns a function
  11373. function utils.add_function_factory (mt,fun)
  11374. _function_factories[mt] = fun
  11375. end
  11376.  
  11377. local function _string_lambda(f)
  11378. local raise = utils.raise
  11379. if f:find '^|' or f:find '_' then
  11380. local args,body = f:match '|([^|]*)|(.+)'
  11381. if f:find '_' then
  11382. args = '_'
  11383. body = f
  11384. else
  11385. if not args then return raise 'bad string lambda' end
  11386. end
  11387. local fstr = 'return function('..args..') return '..body..' end'
  11388. local fn,err = loadstring(fstr)
  11389. if not fn then return raise(err) end
  11390. fn = fn()
  11391. return fn
  11392. else return raise 'not a string lambda'
  11393. end
  11394. end
  11395.  
  11396. --- an anonymous function as a string. This string is either of the form
  11397. -- '|args| expression' or is a function of one argument, '_'
  11398. -- @param lf function as a string
  11399. -- @return a function
  11400. -- @usage string_lambda '|x|x+1' (2) == 3
  11401. -- @usage string_lambda '_+1 (2) == 3
  11402. -- @function utils.string_lambda
  11403. utils.string_lambda = utils.memoize(_string_lambda)
  11404.  
  11405. local ops
  11406.  
  11407. --- process a function argument.
  11408. -- This is used throughout Penlight and defines what is meant by a function:
  11409. -- Something that is callable, or an operator string as defined by <code>pl.operator</code>,
  11410. -- such as '>' or '#'. If a function factory has been registered for the type, it will
  11411. -- be called to get the function.
  11412. -- @param idx argument index
  11413. -- @param f a function, operator string, or callable object
  11414. -- @param msg optional error message
  11415. -- @return a callable
  11416. -- @raise if idx is not a number or if f is not callable
  11417. -- @see utils.is_callable
  11418. function utils.function_arg (idx,f,msg)
  11419. utils.assert_arg(1,idx,'number')
  11420. local tp = type(f)
  11421. if tp == 'function' then return f end -- no worries!
  11422. -- ok, a string can correspond to an operator (like '==')
  11423. if tp == 'string' then
  11424. if not ops then ops = require 'pl.operator'.optable end
  11425. local fn = ops[f]
  11426. if fn then return fn end
  11427. local fn, err = utils.string_lambda(f)
  11428. if not fn then error(err..': '..f) end
  11429. return fn
  11430. elseif tp == 'table' or tp == 'userdata' then
  11431. local mt = getmetatable(f)
  11432. if not mt then error('not a callable object',2) end
  11433. local ff = _function_factories[mt]
  11434. if not ff then
  11435. if not mt.__call then error('not a callable object',2) end
  11436. return f
  11437. else
  11438. return ff(f) -- we have a function factory for this type!
  11439. end
  11440. end
  11441. if not msg then msg = " must be callable" end
  11442. if idx > 0 then
  11443. error("argument "..idx..": "..msg,2)
  11444. else
  11445. error(msg,2)
  11446. end
  11447. end
  11448.  
  11449. --- bind the first argument of the function to a value.
  11450. -- @param fn a function of at least two values (may be an operator string)
  11451. -- @param p a value
  11452. -- @return a function such that f(x) is fn(p,x)
  11453. -- @raise same as @{function_arg}
  11454. -- @see pl.func.curry
  11455. function utils.bind1 (fn,p)
  11456. fn = utils.function_arg(1,fn)
  11457. return function(...) return fn(p,...) end
  11458. end
  11459.  
  11460. --- bind the second argument of the function to a value.
  11461. -- @param fn a function of at least two values (may be an operator string)
  11462. -- @param p a value
  11463. -- @return a function such that f(x) is fn(x,p)
  11464. -- @raise same as @{function_arg}
  11465. function utils.bind2 (fn,p)
  11466. fn = utils.function_arg(1,fn)
  11467. return function(x,...) return fn(x,p,...) end
  11468. end
  11469.  
  11470.  
  11471. --- assert that the given argument is in fact of the correct type.
  11472. -- @param n argument index
  11473. -- @param val the value
  11474. -- @param tp the type
  11475. -- @param verify an optional verfication function
  11476. -- @param msg an optional custom message
  11477. -- @param lev optional stack position for trace, default 2
  11478. -- @raise if the argument n is not the correct type
  11479. -- @usage assert_arg(1,t,'table')
  11480. -- @usage assert_arg(n,val,'string',path.isdir,'not a directory')
  11481. function utils.assert_arg (n,val,tp,verify,msg,lev)
  11482. if type(val) ~= tp then
  11483. error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2)
  11484. end
  11485. if verify and not verify(val) then
  11486. error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2)
  11487. end
  11488. end
  11489.  
  11490. --- assert the common case that the argument is a string.
  11491. -- @param n argument index
  11492. -- @param val a value that must be a string
  11493. -- @raise val must be a string
  11494. function utils.assert_string (n,val)
  11495. utils.assert_arg(n,val,'string',nil,nil,3)
  11496. end
  11497.  
  11498. local err_mode = 'default'
  11499.  
  11500. --- control the error strategy used by Penlight.
  11501. -- Controls how <code>utils.raise</code> works; the default is for it
  11502. -- to return nil and the error string, but if the mode is 'error' then
  11503. -- it will throw an error. If mode is 'quit' it will immediately terminate
  11504. -- the program.
  11505. -- @param mode - either 'default', 'quit' or 'error'
  11506. -- @see utils.raise
  11507. function utils.on_error (mode)
  11508. if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then
  11509. err_mode = mode
  11510. else
  11511. -- fail loudly
  11512. if err_mode == 'default' then err_mode = 'error' end
  11513. utils.raise("Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'")
  11514. end
  11515. end
  11516.  
  11517. --- used by Penlight functions to return errors. Its global behaviour is controlled
  11518. -- by <code>utils.on_error</code>
  11519. -- @param err the error string.
  11520. -- @see utils.on_error
  11521. function utils.raise (err)
  11522. if err_mode == 'default' then return nil,err
  11523. elseif err_mode == 'quit' then utils.quit(err)
  11524. else error(err,2)
  11525. end
  11526. end
  11527.  
  11528. raise = utils.raise
  11529.  
  11530. --- load a code string or bytecode chunk.
  11531. -- @param code Lua code as a string or bytecode
  11532. -- @param name for source errors
  11533. -- @param mode kind of chunk, 't' for text, 'b' for bytecode, 'bt' for all (default)
  11534. -- @param env the environment for the new chunk (default nil)
  11535. -- @return compiled chunk
  11536. -- @return error message (chunk is nil)
  11537. -- @function utils.load
  11538.  
  11539.  
  11540. --- Lua 5.2 Compatible Functions
  11541. -- @section lua52
  11542.  
  11543. --- pack an argument list into a table.
  11544. -- @param ... any arguments
  11545. -- @return a table with field n set to the length
  11546. -- @return the length
  11547. -- @function table.pack
  11548.  
  11549. ------
  11550. -- return the full path where a Lua module name would be matched.
  11551. -- @param mod module name, possibly dotted
  11552. -- @param path a path in the same form as package.path or package.cpath
  11553. -- @see path.package_path
  11554. -- @function package.searchpath
  11555.  
  11556. return utils
  11557.  
  11558.  
  11559. ]===================],"CoolisOS/bin/utils/search",true,"CoolisOS/bin/utils/search/init.lua",[===================[---filesystem search API by CoolisTheName007
  11560. local fs=fs
  11561. local string=string
  11562. local env=getfenv()
  11563. setmetatable(env,nil)
  11564. --The hard work here was writing the iterators to be efficient; for instance they don't use coroutines or recursion.
  11565.  
  11566. ----DIRECTORY TREE-LIKE SEARCH
  11567.  
  11568. ---iterator over files/dirs in directory dir and with maximum depth of search depth, default is infinite
  11569. --following refer to the iterator returned, not the iterator created.
  11570. --@treturn string path
  11571. function iterTree(dir,depth)
  11572. dir=dir or ''
  11573. if dir=='/' then dir='' end
  11574. local index={0}
  11575. local dir_index={0}
  11576. local ts={{dir,fs.list(dir),{}}}
  11577. local level=1
  11578. local t_dir
  11579. return function()
  11580. repeat
  11581. index[level]=index[level]+1
  11582. name=ts[level][2][index[level]]
  11583. -- pprint(index,name)
  11584. -- read()
  11585. if name==nil then
  11586. if (not ts[level][4]) and ts[level][3][1] then
  11587. ts[level][4]=true
  11588. ts[level][2],ts[level][3]=ts[level][3],ts[level][2]
  11589. index[level]=0
  11590. else
  11591. level=level-1
  11592. if level==0 then return end
  11593. dir=ts[level][1]
  11594. end
  11595. else
  11596. t_dir=ts[level][1]..'/'..name
  11597. if fs.isDir(t_dir) then
  11598. if ts[level][4] then
  11599. if depth~=level then
  11600. level=level+1
  11601. dir=t_dir
  11602. ts[level]={dir,fs.list(dir),{}}
  11603. index[level]=0
  11604. dir_index[level]=0
  11605. end
  11606. else
  11607. dir_index[level]=dir_index[level]+1
  11608. ts[level][3][dir_index[level]]=name
  11609. break --send dir path
  11610. end
  11611. else
  11612. break
  11613. end
  11614. end
  11615. until false
  11616. return dir..'/'..name
  11617. end
  11618. end
  11619.  
  11620. ---iterator over files in directory dir and with maximum depth of search depth, default is infinite
  11621. --following refer to the iterator returned, not the iterator created.
  11622. --@treturn string path
  11623. function iterFiles(dir,depth)
  11624. local iter=iterTree(dir,depth)
  11625. local path
  11626. return function()
  11627. repeat
  11628. path=iter()
  11629. if path then
  11630. if not fs.isDir(path) then
  11631. return path
  11632. end
  11633. else
  11634. return
  11635. end
  11636. until false
  11637. end
  11638. end
  11639.  
  11640. ---iterator over dirs in directory dir and with maximum depth of search depth, default is infinite
  11641. --following refer to the iterator returned, not the iterator created.
  11642. --@treturn string path
  11643. function iterDir(dir,depth)
  11644. local iter=iterTree(dir,depth)
  11645. local path
  11646. return function()
  11647. repeat
  11648. path=iter()
  11649. if path then
  11650. if fs.isDir(path) then
  11651. return path
  11652. end
  11653. else
  11654. return
  11655. end
  11656. until false
  11657. end
  11658. end
  11659.  
  11660. ------GLOBS
  11661.  
  11662. --WARNING: for compatibility with Lua ?, ? was replaced by # in globs; taken from https://github.com/davidm/lua-glob-pattern , by davidm
  11663. --only needed for filename conversion, slashes are dealt with directly for iteration purposes.
  11664. local function globtopattern(g)
  11665.  
  11666. local p = "^" -- pattern being built
  11667. local i = 0 -- index in g
  11668. local c -- char at index i in g.
  11669.  
  11670.  
  11671. -- unescape glob char
  11672. local function unescape()
  11673. if c == '\\' then
  11674. i = i + 1; c = string.sub(g,i,i)
  11675. if c == '' then
  11676. p = '[^]'
  11677. return false
  11678. end
  11679. end
  11680. return true
  11681. end
  11682.  
  11683. -- escape pattern char
  11684. local function escape(c)
  11685. return c:match("^%w$") and c or '%' .. c
  11686. end
  11687. -- Convert tokens at end of charset.
  11688. local function charset_end()
  11689. while 1 do
  11690. if c == '' then
  11691. p = '[^]'
  11692. return false
  11693. elseif c == ']' then
  11694. p = p .. ']'
  11695. break
  11696. else
  11697. if not unescape() then break end
  11698. local c1 = c
  11699. i = i + 1; c = string.sub(g,i,i)
  11700. if c == '' then
  11701. p = '[^]'
  11702. return false
  11703. elseif c == '-' then
  11704. i = i + 1; c = string.sub(g,i,i)
  11705. if c == '' then
  11706. p = '[^]'
  11707. return false
  11708. elseif c == ']' then
  11709. p = p .. escape(c1) .. '%-]'
  11710. break
  11711. else
  11712. if not unescape() then break end
  11713. p = p .. escape(c1) .. '-' .. escape(c)
  11714. end
  11715. elseif c == ']' then
  11716. p = p .. escape(c1) .. ']'
  11717. break
  11718. else
  11719. p = p .. escape(c1)
  11720. i = i - 1 -- put back
  11721. end
  11722. end
  11723. i = i + 1; c = string.sub(g,i,i)
  11724. end
  11725. return true
  11726. end
  11727.  
  11728. -- Convert tokens in charset.
  11729. local function charset()
  11730. i = i + 1; c = string.sub(g,i,i)
  11731. if c == '' or c == ']' then
  11732. p = '[^]'
  11733. return false
  11734. elseif c == '^' or c == '!' then
  11735. i = i + 1; c = string.sub(g,i,i)
  11736. if c == ']' then
  11737. -- ignored
  11738. else
  11739. p = p .. '[^'
  11740. if not charset_end() then return false end
  11741. end
  11742. else
  11743. p = p .. '['
  11744. if not charset_end() then return false end
  11745. end
  11746. return true
  11747. end
  11748. --Convert tokens.
  11749. while 1 do
  11750. i = i + 1; c = string.sub(g,i,i)
  11751. if c == '' then
  11752. p = p .. '$'
  11753. break
  11754. elseif c == '#' then --?->#
  11755. p = p .. '.'
  11756. elseif c == '*' then
  11757. p = p .. '.*'
  11758. elseif c == '[' then
  11759. if not charset() then break end
  11760. elseif c == '\\' then
  11761. i = i + 1; c = string.sub(g,i,i)
  11762. if c == '' then
  11763. p = p .. '\\$'
  11764. break
  11765. end
  11766. p = p .. escape(c)
  11767. else
  11768. p = p .. escape(c)
  11769. end
  11770. end
  11771. return p
  11772. end
  11773.  
  11774. ---turns a glob into a table structure proper for iterPatterns.
  11775. local function compact(g)
  11776.  
  11777. local nl={}
  11778. local s1
  11779. local n=0
  11780. for c in string.gmatch(g,'[\\/]*([^/\\]+)[\\/]*') do
  11781. if c:match('^[%w%s%.]*$') then
  11782. s1=s1 and s1..'/'..c or c
  11783. else
  11784. n=n+1
  11785. nl[n]={s1,globtopattern(c)}
  11786. s1=nil
  11787. end
  11788. end
  11789.  
  11790. if s1 then
  11791. if n==0 then
  11792. n=n+1
  11793. nl[n]={s1}
  11794. else
  11795. nl[n][3]=s1
  11796. end
  11797. end
  11798. return nl
  11799. end
  11800.  
  11801. ---iterator creator over valid paths defined by a table with the structure: {t1,...,tn}, where ti is:
  11802. --for i<n: {dir,pat} - dir is the directory where to look for names matching the pattern pat
  11803. --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
  11804. --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
  11805. --containing a folder C, and for all one-lettered folders in that folder for a folder aq containing a folder/file named qwerty.
  11806. local function iterPatterns(l)
  11807. local n=#l
  11808. if n==0 then return function () return end end
  11809. if n==1 and not l[1][2] and fs.exists(l[1][1]) then
  11810. local done=false
  11811. return function ()
  11812. if not done then
  11813. done=true
  11814. return l[1][1]
  11815. else
  11816. return
  11817. end
  11818. end
  11819. end
  11820. local dir=l[1][1]
  11821. local index={0}
  11822. local ts
  11823. ts={{dir,fs.isDir(dir) and fs.list(dir) or {}}}
  11824. local level=1
  11825. local t_dir
  11826. local _
  11827. return function()
  11828. repeat
  11829. index[level]=index[level]+1
  11830. name=ts[level][2][index[level]]
  11831. if name==nil then
  11832. index[level]=nil
  11833. level=level-1
  11834. if level==0 then return end
  11835. dir=ts[level][1]
  11836. else
  11837. if string.match(name,l[level][2]) then
  11838. t_dir=dir..'/'..name
  11839. if level==n then
  11840. _=l[level][3]
  11841. if _ then
  11842. t_dir=t_dir..'/'.._
  11843. if fs.exists(t_dir) then
  11844. path=t_dir
  11845. break
  11846. end
  11847. else
  11848. path=t_dir
  11849. break
  11850. end
  11851. elseif fs.isDir(t_dir) then
  11852. level=level+1
  11853. _=l[level][1]
  11854. if _ then
  11855. t_dir=t_dir..'/'.._
  11856. if fs.exists(t_dir) then
  11857. dir=t_dir
  11858. ts[level]={dir,fs.list(dir)}
  11859. index[level]=0
  11860. else
  11861. level=level-1
  11862. end
  11863. else
  11864. dir=t_dir
  11865. ts[level]={dir,fs.list(dir)}
  11866. index[level]=0
  11867. end
  11868. end
  11869. end
  11870. end
  11871. until false
  11872. return path, index
  11873. end
  11874. end
  11875.  
  11876. ---iterator creator, over the valid paths defined by glob @g, e.g */filenumber?to
  11877. -- see the unix part of the table at http://en.wikipedia.org/wiki/Glob_(programming) .
  11878. --@treturn string path to matching of the dir
  11879. --@usage
  11880. --for path in search.iterGlob('*/stuff?/a*') do
  11881. -- print(path)
  11882. --end
  11883. --APIS/stuff1/a.lua
  11884. --var/stuff2/a.var
  11885. function iterGlob(g)
  11886. return iterPatterns(compact(g))
  11887. end
  11888.  
  11889.  
  11890. --iterator over glob @g with ? replaced by @s
  11891. searchGlob = function (g,s)
  11892. g=string.gsub(g,'%?',s)
  11893. local iter=iterGlob(g)
  11894. local path
  11895. return function ()
  11896. repeat
  11897. path=iter()
  11898. if path then
  11899. if not fs.isDir(path) then
  11900. return path
  11901. end
  11902. else
  11903. break
  11904. end
  11905. until false
  11906. end
  11907. end
  11908.  
  11909. function getNameExpansion(s) --returns name and expansion from a filepath @s; special cases: ''->'',nil; '.'-> '','';
  11910. --s string = filename
  11911. --returns: name, expansion
  11912. --Example
  11913. --print(getNameExpansion('filename.lua.kl'))
  11914. --filename
  11915. --lua.kl
  11916. local _,_,name,expa=string.find(s, '([^%./\\]*)%.(.*)$')
  11917. return name or s,expa
  11918. end
  11919. function getDir(s) --returns directory from filepath @s
  11920. return string.match(s,'^(.*)/') or '/'
  11921. end
  11922.  
  11923. --iterator over directory @p searching for @p...@s, where @s is a glob.
  11924. searchTree = function (p,s)
  11925. if not p:match('%?') and fs.isDir(p) then
  11926. if p=='/' then p='' end
  11927. local iter=iterFiles(p)
  11928. local path
  11929. return function ()
  11930. repeat
  11931. path=iter()
  11932. if path then
  11933. if string.match(path,s..'$') and not string.match(path,'[^/]'..s..'$') then
  11934. return path
  11935. end
  11936. else
  11937. break
  11938. end
  11939. until false
  11940. end
  11941. else
  11942. return function() end
  11943. end
  11944. end
  11945.  
  11946. 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
  11947. ]===================],"CoolisOS/ect/log/sched.log",[===================[]===================],"CoolisOS/ect/log/shell.log",[===================[42.30 shell-error: CoolisOS/bin/programs/quarry.lua:165: No peripheral attached
  11948. ]===================],"CoolisOS/ect/startups",true,"CoolisOS/ect/startups/quarry.lua",[===================[os.execute('>'..fs.combine(_INSTALL_PATH,'bin/programs/quarry.lua'))
  11949. 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]===================],
  11950. }
  11951. args={...}
  11952. if args[1]=='help' or args[1]=='-help' or args[1]=='--help' then
  11953.  
  11954. else
  11955. local loc=shell.getRunningProgram()
  11956. local llen=loc:len()
  11957. local nlen=fs.getName(loc):len()
  11958. loc=loc:sub(1,(llen==nlen and 0 or llen-nlen-1))
  11959. print(args[1])
  11960. print(loc)
  11961. local r=args[1] or loc
  11962. for i=1,#t,2 do
  11963. p,v=t[i],t[i+1]
  11964. p=fs.combine(r,p)
  11965. if i~=1 then
  11966. fs.delete(p)
  11967. end
  11968. if type(v)=='boolean' then
  11969. fs.makeDir(p)
  11970. else
  11971. local f=fs.open(p,'w')
  11972. f.write(v)
  11973. f.close()
  11974. end
  11975. end
  11976. local r_s=args[2] and r or ''
  11977. if not fs.exists('startup') then
  11978. fs.delete(fs.combine(r_s,'startup'))
  11979. local f=fs.open(fs.combine(r_s,'startup'),'w')
  11980. f.write(string.format("shell.run('%s')",fs.combine(loc,'CoolisOS/bin/kernel/init.lua')))
  11981. f.close()
  11982. print('Running startup')
  11983. dofile(fs.combine(r_s,'startup'))
  11984. end
  11985. end
Advertisement
Add Comment
Please, Sign In to add comment