SquidDev

Howl.min.lua

Dec 12th, 2014
438
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 62.17 KB | None | 0 0
  1.  
  2. local function e(j)
  3. local x=setmetatable({},{__index=_ENV or getfenv()})if setfenv then setfenv(j,x)end;return j(x)or x end
  4. local t=e(function(j,...)local function x(E)
  5. if type(E)=="string"then return string.format("%q",E)else return tostring(E)end end
  6. local function z(E,T,A,O)local I=""local N=type(E)
  7. if
  8. N=="table"then local S=A[E]
  9. if S then I=I.. (T.."--[[ Object@"..S.." ]] { }")..
  10. "\n"else
  11. S=A.length+1;A[E]=S;A.length=S;I=I..
  12. (T.."--[[ Object@"..S.." ]] {").."\n"
  13. for H,R in pairs(E)do
  14. if
  15. type(H)=="table"then I=I.. (T.."\t{").."\n"I=I..
  16. z(H,T.."\t\t",A,O)I=I..z(R,T.."\t\t",A,O)I=I..
  17. (T.."\t},").."\n"elseif type(R)=="table"then
  18. I=I.. (T..
  19. "\t["..x(H).."] = {").."\n"I=I..z(R,T.."\t\t",A,O)
  20. I=I.. (T.."\t},").."\n"else I=I..
  21. (T.."\t["..x(H).."] = "..x(R)..",").."\n"end end
  22. if O then local H=getmetatable(E)if H then
  23. I=I.. (T.."\tMetatable = {").."\n"I=I..z(H,T.."\t\t",A,O)
  24. I=I.. (T.."\t}").."\n"end end;I=I.. (T.."}").."\n"end else I=I.. (T..x(E)).."\n"end;return I end
  25. local function _(E,T,A)if T==nil then T=true end;return z(E,A or"",{length=0},T)end;return _ end)
  26. local a=e(function(j,...)local x,z=os.queueEvent,coroutine.yield;local function _()x("sleep")if z()=="terminate"then
  27. error("Terminated")end end;return
  28. {dir=shell.dir,refreshYield=_,serialize=textutils.serialize}end)
  29. local o=e(function(j,...)local x=false;local function z(C,...)local M=term.isColor()
  30. if M then term.setTextColor(C)end;print(...)
  31. if M then term.setTextColor(colors.white)end end
  32. local function _(C,M)
  33. local F=term.isColor()if F then term.setTextColor(C)end;io.write(M)if F then
  34. term.setTextColor(colors.white)end end;local function E(...)z(colors.green,...)end;local function T(...)
  35. z(colors.red,...)end;local function A(C)if C~=nil then x=C end;return x end
  36. local function O(...)if x then local C,M=pcall(function()
  37. error("",4)end)_(colors.gray,M)
  38. z(colors.lightGray,...)end end
  39. local function I(...)
  40. if x then local C,M=pcall(function()error("",4)end)
  41. _(colors.gray,M)local F=false
  42. for C,W in ipairs({...})do local Y=type(W)if Y=="table"then local P=t or a.serialize
  43. W=P(W)else W=tostring(W)end;if F then W=" "..W end
  44. F=true;_(colors.lightGray,W)end;print()end end
  45. local N={["^"]="%^",["$"]="%$",["("]="%(",[")"]="%)",["%"]="%%",["."]="%.",["["]="%[",["]"]="%]",["*"]="%*",["+"]="%+",["-"]="%-",["?"]="%?",["\0"]="%z"}local function S(C)return(C:gsub(".",N))end
  46. local H={["^"]="%^",["$"]="%$",["("]="%(",[")"]="%)",["%"]="%%",["."]="%.",["["]="%[",["]"]="%]",["+"]="%+",["-"]="%-",["?"]="%?",["\0"]="%z"}
  47. local function R(C,M)local F=C:sub(1,5)
  48. if F=="ptrn:"or F=="wild:"then local C=C:sub(6)
  49. if F=="wild:"then
  50. if M then
  51. local W=0
  52. C=((C:gsub(".",H)):gsub("(%*)",function()W=W+1;return"%"..W end))else C="^"..
  53. ((C:gsub(".",H)):gsub("(%*)","(.*)")).."$"end end;return{Type="Pattern",Text=C}else return{Type="Normal",Text=C}end end;local function D(C)for M,F in ipairs(C)do C[F]=true end;return C end;local function L(C,M)
  54. local F=#C;if F~=#M then return false end
  55. for W=1,F do if C[W]~=M[W]then return false end end;return true end
  56. local U=print
  57. return
  58. {Print=U,PrintError=T,PrintSuccess=E,PrintColor=z,WriteColor=_,IsVerbose=A,Verbose=O,VerboseLog=I,EscapePattern=S,ParsePattern=R,CreateLookup=D,MatchTables=L}end)
  59. local i=e(function(j,...)local x,z=type,pairs;local _={}
  60. function _:update(D)if D then self.fn=D.fn or self.fn;self.options=D.options or
  61. self.options end end
  62. local function E(D,L)return
  63. setmetatable({options=L or{},fn=D,channel=nil,id=math.random(1000000000)},{__index=_})end;local T={}
  64. local function A(D,L)return
  65. setmetatable({stopped=false,namespace=D,callbacks={},channels={},parent=L},{__index=T})end
  66. function T:addSubscriber(D,L)local U=E(D,L)local C=(#self.callbacks+1)
  67. L=L or{}if L.priority and L.priority>=0 and L.priority<C then
  68. C=L.priority end
  69. table.insert(self.callbacks,C,U)return U end
  70. function T:getSubscriber(D)for U=1,#self.callbacks do local C=self.callbacks[U]if C.id==D then
  71. return{index=U,value=C}end end;local L
  72. for U,C in
  73. z(self.channels)do L=C:getSubscriber(D)if L then break end end;return L end
  74. function T:setPriority(D,L)local U=self:getSubscriber(D)
  75. if U.value then
  76. table.remove(self.callbacks,U.index)table.insert(self.callbacks,L,U.value)end end
  77. function T:addChannel(D)self.channels[D]=A(D,self)return self.channels[D]end
  78. function T:hasChannel(D)return D and self.channels[D]and true end;function T:getChannel(D)
  79. return self.channels[D]or self:addChannel(D)end
  80. function T:removeSubscriber(D)
  81. local L=self:getSubscriber(D)
  82. if L and L.value then
  83. for U,C in z(self.channels)do C:removeSubscriber(D)end;return table.remove(self.callbacks,L.index)end end
  84. function T:publish(D,...)
  85. for L=1,#self.callbacks do local U=self.callbacks[L]
  86. if
  87. not U.options.predicate or U.options.predicate(...)then local C,M=U.fn(...)if M then
  88. D[#D]=M end;if C==false then return false,D end end end
  89. if parent then return parent:publish(D,...)else return true,D end end;local O=A('root')
  90. local function I(D)local O=O
  91. if x(D)=="string"then if D:find(":")then
  92. D={D:match((D:gsub("[^:]+:?","([^:]+):?")))}else D={D}end end;for L=1,#D do O=O:getChannel(D[L])end;return O end;local function N(D,L,U)return I(D):addSubscriber(L,U)end;local function S(D,L)return
  93. I(L):getSubscriber(D)end;local function H(D,L)
  94. return I(L):removeSubscriber(D)end
  95. local function R(D,...)return I(D):publish({},...)end
  96. return{GetChannel=I,Subscribe=N,GetSubscriber=S,RemoveSubscriber=H,Publish=R}end)
  97. local n=e(function(j,...)
  98. local x={__index=function(E,T)return
  99. function(E,...)local A=E.parser;local O=A[T](A,E.name,...)if O==A then return E end;return O end end}local z={}
  100. function z:Get(E,T)local A=self.options;local O=A[E]if O~=nil then return O end
  101. local I=self.settings[E]
  102. if I then local N=I.aliases;if N then
  103. for S,H in ipairs(N)do O=A[H]if O~=nil then return O end end end;O=I.default;if O~=nil then return O end end;return T end;function z:Ensure(E)local T=self:Get(E)
  104. if T==nil then error(E.." must be set")end;return T end;function z:Default(E,T)
  105. if T==nil then T=true end;self:_SetSetting(E,"default",T)self:_Changed()
  106. return self end
  107. function z:Alias(E,T)
  108. local A=self.settings;local O=A[E]if O then local I=O.aliases
  109. if I==nil then O.aliases={T}else table.insert(I,T)end else A[E]={aliases={T}}end
  110. self:_Changed()return self end
  111. function z:Description(E,T)return self:_SetSetting(E,"description",T)end;function z:TakesValue(E,T)if T==nil then T=true end
  112. return self:_SetSetting(E,"takesValue",T)end
  113. function z:_SetSetting(E,T,A)local O=self.settings
  114. local I=O[E]if I then I[T]=A else O[E]={[T]=A}end;return self end
  115. function z:Option(E)return setmetatable({name=E,parser=self},x)end;function z:Arguments()return self.arguments end;function z:_Changed()
  116. i.Publish({"ArgParse","changed"},self)end
  117. function z:Help(E)
  118. for T,A in pairs(self.settings)do local O='-'if A.takesValue then O="--"T=
  119. T.."=value"end;if#T>1 then O='--'end;o.WriteColor(colors.white,E..
  120. O..T)local I=""local N=A.aliases
  121. if N and#N>0 then
  122. local H=#N;I=I.." ("for R=1,H do local D="-"..N[R]if#D>2 then D="-"..D end
  123. if R<H then D=D..', 'end;I=I..D end;I=I..")"end;o.WriteColor(colors.brown,I)local S=A.description;if S and S~=""then o.PrintColor(colors.lightGray,
  124. " "..S)end end end
  125. function z:Parse(E)local T=self.options;local A=self.arguments
  126. for O,I in ipairs(E)do
  127. if I:sub(1,1)=="-"then
  128. if
  129. I:sub(2,2)=="-"then local N,S=I:match("([%w_%-]+)=([%w_%-]+)",3)if N then T[N]=S else
  130. I=I:sub(3)local H=I:sub(1,4)local S=true
  131. if H=="not-"or H=="not_"then S=false;I=I:sub(5)end;T[I]=S end else for N=2,#I do
  132. T[I:sub(N,N)]=true end end else table.insert(A,I)end end;return self end
  133. local function _(E)return
  134. setmetatable({options={},arguments={},settings={}},{__index=z}):Parse(E)end;return{Parser=z,Options=_}end)
  135. local s=e(function(j,...)local x={}
  136. function x:DoRequire(E,T)if self.filesProduced[E]then return true end
  137. local A=self.producesCache[E]
  138. if A then self.filesProduced[E]=true;return self:Run(A)end;A=self.normalMapsCache[E]local O,I;local N=E;if A then
  139. self.filesProduced[E]=true;I=A.Name;O=A.Pattern.From end
  140. for S,H in
  141. pairs(self.patternMapsCache)do if E:match(S)then self.filesProduced[E]=true;I=H.Name
  142. O=E:gsub(S,H.Pattern.From)break end end
  143. if I then local S=self:DoRequire(O,true)
  144. if not S then if not T then
  145. o.PrintError("Cannot find '"..O.."'")end;return false end;return self:Run(I,O,N)end;if fs.exists(fs.combine(self.env.CurrentDirectory,E))then
  146. self.filesProduced[E]=true;return true end;if not T then
  147. o.PrintError(
  148. "Cannot find a task matching '"..E.."'")end;return false end;local function z(E,T)local A=#E;if#E~=#T then return false end
  149. for O=1,A do if E[O]~=T[O]then return false end end;return true end
  150. function x:Run(E,...)
  151. local T=E
  152. if type(E)=="string"then T=self.tasks[E]if not T then
  153. o.PrintError("Cannot find a task called '"..E.."'")return false end elseif not T or not T.Run then
  154. o.PrintError("Cannot call task as it has no 'Run' method")return false end;local A={...}local O=self.ran[T]
  155. if not O then O={A}self.ran[T]=O else for I=1,#O do if z(A,O[I])then
  156. return true end end;O[#O+1]=A end;a.refreshYield()return T:Run(self,...)end
  157. function x:Start(E)local T
  158. if E then T=self.tasks[E]else T=self.default;E="<default>"end;if not T then
  159. o.PrintError("Cannot find a task called '"..E.."'")return false end;return self:Run(T)end
  160. function x:BuildCache()local E={}local T={}local A={}self.producesCache=E;self.patternMapsCache=T
  161. self.normalMapsCache=A
  162. for O,I in pairs(self.tasks)do local N=I.produces;if N then
  163. for H,R in ipairs(N)do local D=E[R]if D then
  164. error(string.format("Both '%s' and '%s' produces '%s'",D,O,R))end;E[R]=O end end
  165. local S=I.maps
  166. if S then
  167. for H,R in ipairs(S)do
  168. local D=(R.Type=="Pattern"and T or A)local L=R.To;local U=D[L]if U then
  169. error(string.format("Both '%s' and '%s' match '%s'",U,O,L))end;D[L]={Name=O,Pattern=R}end end end;return self end
  170. local function _(E)
  171. return
  172. setmetatable({ran={},filesProduced={},tasks=E.tasks,default=E.default,Traceback=E.Traceback,ShowTime=E.ShowTime,env=E.env},{__index=x}):BuildCache()end;return{Factory=_,Context=x}end)
  173. local h=e(function(j,...)
  174. local function x(T,A)local O=o.ParsePattern(T,true)local I=o.ParsePattern(A)
  175. local N=O.Type
  176. assert(N==I.Type,"Both from and to must be the same type "..N.." and "..O.Type)return{Type=N,From=O.Text,To=I.Text}end;local z={}
  177. function z:Depends(T)
  178. if type(T)=="table"then local A=self.dependencies;for O,I in ipairs(T)do
  179. table.insert(A,I)end else table.insert(self.dependencies,T)end;return self end
  180. function z:Requires(T)
  181. if type(T)=="table"then local A=self.requires;for O,T in ipairs(T)do
  182. table.insert(A,T)end else table.insert(self.requires,T)end;return self end
  183. function z:Produces(T)
  184. if type(T)=="table"then local A=self.produces;for O,T in ipairs(T)do
  185. table.insert(A,T)end else table.insert(self.produces,T)end;return self end
  186. function z:Maps(T,A)table.insert(self.maps,x(T,A))return self end;function z:Action(T)self.action=T;return self end;function z:Description(T)
  187. self.description=T;return self end;function z:_RunAction(T,...)
  188. return self.action(self,T,...)end
  189. function z:Run(T,...)for A,O in ipairs(self.dependencies)do if not T:Run(O)then return
  190. false end end
  191. for A,O in
  192. ipairs(self.requires)do if not T:DoRequire(O)then return false end end
  193. for A,O in ipairs(self.produces)do T.filesProduced[O]=true end
  194. if self.action then local A={...}local O=""if#A>0 then local H={}for R,D in ipairs(A)do
  195. table.insert(H,tostring(D))end
  196. O=" ("..table.concat(H,", ")..")"end
  197. o.PrintColor(colors.cyan,
  198. "Running "..self.name..O)local I=os.clock()local N,S=true,nil
  199. if T.Traceback then
  200. xpcall(function()
  201. self:_RunAction(T.env,unpack(A))end,function(H)
  202. for R=5,15 do
  203. local D,S=pcall(function()error("",R)end)if H:match("Howlfile")then break end;H=H.."\n  "..S end;S=H;N=false end)else N,S=pcall(self._RunAction,self,T.env,...)end
  204. if N then o.PrintSuccess(self.name..": Success")else o.PrintError(
  205. self.name..": Failure\n"..S)end;if T.ShowTime then
  206. o.Print(" ","Took "..os.clock()-I.."s")end;return N end;return true end
  207. local _={__index=function(T,A)local O=z[A]if O then return O end
  208. if A:match("^%u")then local I=z[A]if I then return I end
  209. return function(T,N)
  210. if N==nil then N=true end;T[(A:gsub("^%u",string.lower))]=N;return
  211. T end end end}
  212. local function E(T,A,O,I)if type(A)=="function"then O=A;A={}end
  213. return setmetatable({name=T,action=O,dependencies=A or{},description=nil,maps={},requires={},produces={}},
  214. I or{__index=z})end;return{Factory=E,Task=z,OptionTask=_}end)
  215. local r=e(function(j,...)local x={}function x:Task(_)
  216. return function(E,T)return self:AddTask(_,E,T)end end;function x:AddTask(_,E,T)return
  217. self:InjectTask(h.Factory(_,E,T))end;function x:InjectTask(_,E)
  218. self.tasks[E or _.name]=_;return _ end
  219. function x:Default(_)local E
  220. if _==nil then self.default=nil elseif type(_)==
  221. "string"then self.default=self.tasks[_]if not self.default then
  222. error("Cannot find task ".._)end else
  223. self.default=h.Factory("<default>",{},_)end;return self end;function x:Run(_)return self:RunMany({_})end
  224. function x:RunMany(_)
  225. local E=os.clock()local T;local A=s.Factory(self)if#_==0 then A:Start()else
  226. for O,I in ipairs(_)do T=A:Start(I)end end;if A.ShowTime then
  227. o.PrintColor(colors.orange,"Took "..
  228. os.clock()-E.."s in total")end;return T end;local function z(_)
  229. return setmetatable({tasks={},default=nil,env=_},{__index=x})end;return{Factory=z,Runner=x}end)
  230. local d=e(function(j,...)local x={"Howlfile","Howlfile.lua"}
  231. local function z()local T=a.dir()
  232. while true do for A,O in ipairs(x)do
  233. local I=fs.combine(T,O)
  234. if fs.exists(I)and not fs.isDir(I)then return O,T end end
  235. if T=="/"or T==""then break end;T=fs.getDir(T)end;return nil,
  236. "Cannot find HowlFile. Looking for '"..table.concat(x,"', '").."'"end
  237. local function _(T)local A=setmetatable(T or{},{__index=getfenv()})
  238. A._G=_G;function A.loadfile(O)return setfenv(loadfile(O),A)end;function A.dofile(O)return
  239. A.loadfile(O)()end
  240. i.Publish({"HowlFile","env"},A)return A end
  241. local function E(T,A,O)local I=r.Factory({CurrentDirectory=T,Options=O})
  242. i.Subscribe({"ArgParse","changed"},function(O)
  243. I.ShowTime=O:Get"time"I.Traceback=O:Get"trace"end)
  244. local N=_({CurrentDirectory=T,Tasks=I,Options=O,Verbose=o.Verbose,Log=o.VerboseLog,File=function(...)return fs.combine(T,...)end})return I,N end;return{FindHowl=z,SetupEnvironment=_,SetupTasks=E,Names=x}end)
  245. do
  246. function r.Runner:ListTasks(j,x)local z={}local _=0
  247. for E,T in pairs(self.tasks)do local A=E:sub(1,1)
  248. if x or
  249. (A~="_"and A~=".")then local O=T.description or""local I=#E;if I>_ then _=I end;z[E]=O end end;_=_+2;j=j or""for E,T in pairs(z)do o.WriteColor(colors.white,j..E)
  250. o.PrintColor(colors.lightGray,string.rep(" ",
  251. _-#E)..T)end;return self end
  252. function r.Runner:Clean(j,x,z)
  253. return
  254. self:AddTask(j,z,function(_,E)
  255. o.Verbose("Emptying directory '"..x.."'")local T=fs.combine(E.CurrentDirectory,x)if fs.isDir(T)then
  256. for A,O in
  257. pairs(fs.list(T))do fs.delete(fs.combine(T,O))end else fs.delete(T)end end):Description(
  258. "Clean the '"..x.."' directory")end end
  259. local l=e(function(j,...)local x={}
  260. function x:Name(E)self.name=E;self:Alias(E)return self end;function x:Alias(E)self.alias=E;return self end
  261. function x:Depends(E)if type(E)=="table"then for T,A in
  262. ipairs(E)do self:Depends(A)end else
  263. table.insert(self.dependencies,E)end;return self end
  264. function x:Prerequisite(E)
  265. if type(E)=="table"then
  266. for T,A in ipairs(E)do self:Prerequisite(A)end else table.insert(self.dependencies,1,E)end;return self end
  267. function x:Export(E)if E==nil then E=true end;self.shouldExport=E;return self end
  268. function x:NoWrap(E)if E==nil then E=true end;self.noWrap=E;return self end;local z={}
  269. local function _(E,T)return
  270. setmetatable({mainFiles={},files={},path=E,namespaces={},shouldExport=false,parent=T},{__index=z})end;function z:File(E)local T=self:_File(E)self.files[E]=T
  271. i.Publish({"Dependencies","create"},self,T)return T end;function z:Resource(E)
  272. local T=self:_File(E)T.type="Resource"self.files[E]=T
  273. i.Publish({"Dependencies","create"},self,T)return T end
  274. function z:_File(E)return
  275. setmetatable({dependencies={},name=
  276. nil,alias=nil,path=E,shouldExport=true,noWrap=false,type="File",parent=self},{__index=x})end
  277. function z:Main(E)local T=self:FindFile(E)or self:_File(E)
  278. T.type="Main"table.insert(self.mainFiles,T)
  279. i.Publish({"Dependencies","create"},self,T)return T end
  280. function z:Depends(E)local T=self.mainFiles[1]
  281. assert(T,"Cannot find a main file")T:Depends(E)return self end
  282. function z:Prerequisite(E)local T=self.mainFiles[1]
  283. assert(T,"Cannot find a main file")T:Prerequisite(E)return self end
  284. function z:FindFile(E)local T=self.files;local A=T[E]if A then return A end;A=T[E..".lua"]
  285. if A then return A end;for O,A in pairs(T)do if A.alias==E then return A end end;return nil end
  286. function z:Iterate()local E={}
  287. local function T(O)if E[O.path]then return end;E[O.path]=true;for I,N in ipairs(O.dependencies)do
  288. local S=self:FindFile(N)
  289. if not S then error("Cannot find file "..N)end;T(S)end
  290. coroutine.yield(O)end;local A=self.mainFiles;if#A==0 then A=self.files end
  291. return coroutine.wrap(function()for O,I in pairs(A)do
  292. T(I)end end)end
  293. function z:Export(E)if E==nil then E=true end;self.shouldExport=E;return self end
  294. function z:Namespace(E,T,A)
  295. local O=_(fs.combine(self.path,T or""),self)self.namespaces[E]=O;A(O)return O end
  296. function z:CloneDependencies()local E=setmetatable({},{__index=z})
  297. for T,A in pairs(self)do E[T]=A end;E.mainFiles={}return E end;function z:Paths()local E,T=table.insert,{}
  298. for A,O in pairs(self.files)do E(T,O.path)end;return T end
  299. i.Subscribe({"HowlFile","env"},function(E)E.Dependencies=function(...)return
  300. _(E.CurrentDirectory,...)end
  301. E.Sources=_(E.CurrentDirectory)end)return{File=x,Dependencies=z,Factory=_}end)
  302. do local j=string.format
  303. local x=[[
  304. local args = {...}
  305. xpcall(function()
  306.     (function(...)
  307. ]]
  308. local z=[[
  309.     end)(unpack(args))
  310. end, function(err)
  311.     printError(err)
  312.     for i = 3, 15 do
  313.         local s, msg = pcall(error, "", i)
  314.         if msg:match("xpcall") then break end
  315.         printError("  ", msg)
  316.     end
  317.     error(err:match(":.+"):sub(2), 3)
  318. end)
  319. ]]
  320. local _=[[
  321. local env = setmetatable({}, {__index = getfenv()})
  322. local function openFile(filePath)
  323.     local f = assert(fs.open(filePath, "r"), "Cannot open " .. filePath)
  324.     local contents = f.readAll()
  325.     f.close()
  326.     return contents
  327. end
  328. local function doWithResult(file)
  329.     local currentEnv = setmetatable({}, {__index = env})
  330.     local result = setfenv(assert(loadfile(file), "Cannot find " .. file), currentEnv)()
  331.     if result ~= nil then return result end
  332.     return currentEnv
  333. end
  334. local function doFile(file, ...)
  335.     return setfenv(assert(loadfile(file), "Cannot find " .. file), env)(...)
  336. end
  337. ]]
  338. function l.Dependencies:CreateBootstrap(E,T,A)local O=self.path
  339. local I=fs.open(fs.combine(E.CurrentDirectory,T),"w")assert(I,"Could not create"..T)if A.traceback then
  340. I.writeLine(x)end;I.writeLine(_)
  341. for N in self:Iterate()do
  342. local S=j("%q",fs.combine(O,N.path))local H=N.name
  343. if N.type=="Main"then
  344. I.writeLine("doFile("..S..", ...)")elseif N.type=="Resource"then
  345. I.writeLine("env["..j("%q",H)"] = openFile("..S..")")elseif H then
  346. I.writeLine("env["..
  347. j("%q",H).."] = "..
  348. (N.noWrap and"doFile"or"doWithResult").."("..S..")")else I.writeLine("doFile("..S..")")end end;if A.traceback then I.writeLine(z)end;I.close()end
  349. function r.Runner:CreateBootstrap(E,T,A,O)
  350. return
  351. self:InjectTask(h.Factory(E,O,function(I,N)T:CreateBootstrap(N,A,I)end,h.OptionTask)):Description(
  352. "Creates a 'dynamic' combination of files in '"..A.."')"):Produces(A):Requires(T:Paths())end end
  353. do local j=loadstring
  354. i.Subscribe({"Combiner","include"},function(x,z,_,E)
  355. if E.verify and z.verify~=false then
  356. local T,A=j(_)
  357. if not T then local O=z.path;local I="Could not load "..
  358. (O and("file "..O)or"string")if A~="nil"then
  359. I=I..":\n"..A end;return false,I end end end)
  360. i.Subscribe({"Dependencies","create"},function(x,z)
  361. if z.type=="Resource"then z:Verify(false)end end)
  362. function l.File:Verify(x)if x==nil then x=true end;self.verify=x;return self end end
  363. do local j=string.find
  364. local x={header=[[
  365.         -- Maps
  366.         local lineToModule = {{lineToModule}}
  367.         local getLine(line)
  368.             while line >= 0 do
  369.                 local l = lineToModule[line]
  370.                 if l then return l end
  371.                 line = line - 1
  372.             end
  373.             return -1
  374.         })
  375.         local moduleStarts = {{moduleStarts}}
  376.         local programEnd = {{lastLine}}
  377.  
  378.         -- Stores the current file, safer than shell.getRunningProgram()
  379.         local _, currentFile = pcall(error, "", 2)
  380.         currentFile = currentFile:match("[^:]+")
  381.     ]],updateError=[[
  382.         -- If we are in the current file then we should map to the old modules
  383.         if filename == currentFile then
  384.  
  385.             -- If this line is after the program end then
  386.             -- something is broken, and so we just roll with it
  387.             if line > programEnd then return end
  388.  
  389.             -- convert to module lines
  390.             filename = getLine(line) or "<?>"
  391.             local newLine = moduleStarts[filename]
  392.             if newLine then
  393.                 line = line - newLine + 1
  394.             else
  395.                 line = -1
  396.             end
  397.         end
  398.     ]]}
  399. local z={header=[[
  400.         local finalizer = function(message, traceback) {{finalizer}} end
  401.     ]],parseTrace=[[
  402.         local ok, finaliserError = pcall(finalizer, message, traceback)
  403.  
  404.         if not ok then
  405.             printError("Finalizer Error: ", finaliserError)
  406.         end
  407.     ]]}
  408. local _=([[
  409. end
  410. -- The main program executor
  411.     local args = {...}
  412.     local currentTerm = term.current()
  413.     local ok, returns = xpcall(
  414.         function() return {__program(unpack(args))} end,
  415.         function(message)
  416.             local _, err = pcall(function()
  417.             local error, pcall, printError, tostring,setmetatable = error, pcall, printError, tostring, setmetatable
  418.             {{header}}
  419.  
  420.             local messageMeta = {
  421.                 __tostring = function(self)
  422.                     local msg = self[1] or "<?>"
  423.                     if self[2] then msg = msg .. ":" .. tostring(self[2]) end
  424.                     if self[3] and self[3] ~= " " then msg = msg .. ":" .. tostring(self[3]) end
  425.                     return msg
  426.                 end
  427.             }
  428.             local function updateError(err)
  429.                 local filename, line, message = err:match("([^:]+):(%d+):?(.*)")
  430.                 -- Something is really broken if we can't find a filename
  431.                 -- If we can't find a line number than we must have `pcall:` or `xpcall`
  432.                 -- This means, we shouldn't have an error, so we must be debugging somewhere
  433.                 if not filename or not line then return end
  434.                 line = tonumber(line)
  435.                 {{updateError}}
  436.                 return setmetatable({filename, line, message}, messageMeta)
  437.             end
  438.  
  439.             -- Reset terminal
  440.             term.redirect(currentTerm)
  441.  
  442.             -- Build a traceback
  443.             local topError = updateError(message) or message
  444.             local traceback = {topError}
  445.             for i = 6, 6 + 18 do
  446.                 local _, err = pcall(error, "", i)
  447.                 err = updateError(err)
  448.                 if not err then break end
  449.                 traceback[#traceback + 1] = err
  450.             end
  451.  
  452.             {{parseTrace}}
  453.  
  454.             printError(tostring(topError))
  455.             if #traceback > 1 then
  456.                 printError("Raw Stack Trace:")
  457.                 for i = 2, #traceback do
  458.                     printError("  ", tostring(traceback[i]))
  459.                 end
  460.             end
  461.             end)
  462.             if not _ then printError(err) end
  463.         end
  464.     )
  465.  
  466.     if ok then
  467.         return unpack(returns)
  468.     end
  469. ]])
  470. local function E(O)local I,N,S=1,1,1;local H=1;local R=#O;while I<R do N,S=j(O,'\n',I,true)if not N then break end;H=H+1
  471. I=S+1 end;return H end;local function T(O,I)
  472. return O:gsub("{{(.-)}}",function(N)return I[N]or""end)end
  473. i.Subscribe({"Combiner","start"},function(O,I,N)if O.finalizer then
  474. N.traceback=true end;if N.lineMapping then N.oldLine=0;N.line=0;N.lineToModule={}
  475. N.moduleStarts={}end;if N.traceback then
  476. I.write("local __program = function(...)")end end)local A=math.min
  477. i.Subscribe({"Combiner","write"},function(O,I,N,S)
  478. if S.lineMapping then I=I or"file"local H=S.line
  479. S.oldLine=H;local R=H+E(N)S.line=R;H=H+1;R=R-1;local D,L=S.moduleStarts,S.lineToModule
  480. local U=D[I]if U then D[I]=A(H,U)else D[I]=H end;L[A(H,R)]=I end end)
  481. i.Subscribe({"Combiner","end"},function(O,I,N)
  482. if N.traceback then local S={}local H={}
  483. if O.finalizer then local D=O.finalizer.path
  484. local L=fs.combine(O.path,D)
  485. local U=assert(fs.open(L,"r"),"Finalizer "..L.." does not exist")local C=U.readAll()U.close()if#C==0 then C=nil else
  486. i.Publish({"Combiner","include"},O,z,C,N)end
  487. if C then S[#S+1]=z;H.finalizer=C end end
  488. if N.lineMapping then S[#S+1]=x;local D=a.serialize
  489. H.lineToModule=D(N.lineToModule)H.moduleStarts=D(N.moduleStarts)H.lastLine=N.line end;local R={}for D,L in ipairs(S)do
  490. for U,C in pairs(L)do local M=R[U]if M then M=M.."\n"else M=""end;R[U]=M..C end end
  491. I.write(T(T(_,R),H))end end)
  492. function l.Dependencies:Finalizer(O)
  493. local I=self:FindFile(O)or self:File(O)I.type="Finalizer"self.finalizer=I
  494. i.Publish({"Dependencies","create"},self,I)return I end end
  495. do local j=i.GetChannel{"Combiner"}local x="_W"
  496. local z=("local function "..x..
  497. [[(f)
  498.     local e=setmetatable({}, {__index = _ENV or getfenv()})
  499.     if setfenv then setfenv(f, e) end
  500.     return f(e) or e
  501. end]]):gsub("[\t\n ]+"," ")
  502. function l.Dependencies:Combiner(_,E,T)T=T or{}local A=self.path;local O=self.shouldExport
  503. local I=a.serialize
  504. local N=fs.open(fs.combine(_.CurrentDirectory,E),"w")assert(N,"Could not create "..E)
  505. local S=j:getChannel("include")local H,R;do local L=N.writeLine;local U=j:getChannel("write")local C=U.publish;R=function(M,F)if
  506. C(U,{},self,F,M,T)then L(M)end end
  507. H={write=R,path=E}end
  508. j:getChannel("start"):publish({},self,H,T)if T.header~=false then R(z)end;local D={}
  509. for L in self:Iterate()do local U=L.path
  510. local C=fs.open(fs.combine(A,U),"r")
  511. assert(C,"File "..U.." does not exist")local M=C.readAll()C.close()local F,W=S:publish({},self,L,M,T)
  512. if not F then
  513. N.close()error(W[#W]or"Unknown error")end;o.Verbose("Adding "..U)local Y=L.name
  514. if L.type=="Main"then
  515. R(M,L.alias or L.path)elseif L.type=="Resource"then local P=
  516. assert(Y,"A name must be specified for resource "..L.path).."="
  517. if not
  518. L.shouldExport then P="local "..P elseif not O then D[#D+1]=Y;P="local "..P end;R(P..I(M),L.alias or L.path)elseif Y then
  519. local P,V=x..'(function(_ENV, ...)','end)'if L.noWrap then P,V='(function(...)','end)()'end;local B=Y..'='..P
  520. if not
  521. L.shouldExport then B="local "..B elseif not O then D[#D+1]=Y;B="local "..B end;R(B)R(M,Y)R(V)else local P=not L.noWrap;if P then R("do")end
  522. R(M,L.alias or L.path)if P then R('end')end end end
  523. if#D>0 and#self.mainFiles==0 then local L={}for U,C in ipairs(D)do L[#L+1]=C..
  524. "="..C..", "end;R("return {"..
  525. table.concat(L).."}")end
  526. j:getChannel("end"):publish({},self,H,T)N.close()end
  527. function r.Runner:Combine(_,E,T,A)
  528. return
  529. self:InjectTask(h.Factory(_,A,function(O,I)E:Combiner(I,T,O)end,h.OptionTask)):Description(
  530. "Combines files into '"..T.."'"):Produces(T):Requires(E:Paths())end end
  531. local u=e(function(j,...)createLookup=o.CreateLookup
  532. WhiteChars=createLookup{' ','\n','\t','\r'}
  533. EscapeLookup={['\r']='\\r',['\n']='\\n',['\t']='\\t',['"']='\\"',["'"]="\\'"}
  534. LowerChars=createLookup{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}
  535. UpperChars=createLookup{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
  536. Digits=createLookup{'0','1','2','3','4','5','6','7','8','9'}
  537. HexDigits=createLookup{'0','1','2','3','4','5','6','7','8','9','A','a','B','b','C','c','D','d','E','e','F','f'}
  538. Symbols=createLookup{'+','-','*','/','^','%',',','{','}','[',']','(',')',';','#'}
  539. Keywords=createLookup{'and','break','do','else','elseif','end','false','for','function','goto','if','in','local','nil','not','or','repeat','return','then','true','until','while'}
  540. StatListCloseKeywords=createLookup{'end','else','elseif','until'}UnOps=createLookup{'-','not','#'}end)
  541. local c=e(function(j,...)local x=u.Keywords;local z={}
  542. function z:AddLocal(E)table.insert(self.Locals,E)end
  543. function z:CreateLocal(E)local T=self:GetLocal(E)if T then return T end
  544. T={Scope=self,Name=E,IsGlobal=false,CanRename=true,References=1}self:AddLocal(T)return T end;function z:GetLocal(E)
  545. for T,A in pairs(self.Locals)do if A.Name==E then return A end end
  546. if self.Parent then return self.Parent:GetLocal(E)end end;function z:GetOldLocal(E)if
  547. self.oldLocalNamesMap[E]then return self.oldLocalNamesMap[E]end;return
  548. self:GetLocal(E)end
  549. function z:RenameLocal(E,T)E=
  550. type(E)=='string'and E or E.Name;local A=false
  551. local O=self:GetLocal(E)
  552. if O then O.Name=T;self.oldLocalNamesMap[E]=O;A=true end
  553. if not A and self.Parent then self.Parent:RenameLocal(E,T)end end
  554. function z:AddGlobal(E)table.insert(self.Globals,E)end
  555. function z:CreateGlobal(E)local T=self:GetGlobal(E)if T then return T end
  556. T={Scope=self,Name=E,IsGlobal=true,CanRename=true,References=1}self:AddGlobal(T)return T end;function z:GetGlobal(E)
  557. for T,A in pairs(self.Globals)do if A.Name==E then return A end end
  558. if self.Parent then return self.Parent:GetGlobal(E)end end
  559. function z:GetOldGlobal(E)
  560. if
  561. self.oldGlobalNamesMap[E]then return self.oldGlobalNamesMap[E]end;return self:GetGlobal(E)end
  562. function z:RenameGlobal(E,T)E=type(E)=='string'and E or E.Name
  563. local A=false;local O=self:GetGlobal(E)if O then O.Name=T
  564. self.oldGlobalNamesMap[E]=O;A=true end;if not A and self.Parent then
  565. self.Parent:RenameGlobal(E,T)end end;function z:GetVariable(E)
  566. return self:GetLocal(E)or self:GetGlobal(E)end
  567. function z:GetOldVariable(E)return self:GetOldLocal(E)or
  568. self:GetOldGlobal(E)end
  569. function z:RenameVariable(E,T)
  570. E=type(E)=='string'and E or E.Name;if self:GetLocal(E)then self:RenameLocal(E,T)else
  571. self:RenameGlobal(E,T)end end;function z:GetAllVariables()
  572. return self:getVars(true,self:getVars(true))end
  573. function z:getVars(E,T)local T=T or{}
  574. if E then for A,O in
  575. pairs(self.Children)do O:getVars(true,T)end else for A,O in pairs(self.Locals)do
  576. table.insert(T,O)end
  577. for A,O in pairs(self.Globals)do table.insert(T,O)end
  578. if self.Parent then self.Parent:getVars(false,T)end end;return T end
  579. function z:ObfuscateLocals(E)
  580. local T=E or"etaoinshrdlucmfwypvbgkqjxz_ETAOINSHRDLUCMFWYPVBGKQJXZ"
  581. local A=E or"etaoinshrdlucmfwypvbgkqjxz_0123456789ETAOINSHRDLUCMFWYPVBGKQJXZ"local O,I=#T,#A;local N=0;local S=math.floor
  582. for H,R in pairs(self.Locals)do local D
  583. repeat
  584. if N<O then N=N+1
  585. D=T:sub(N,N)else
  586. if N<O then N=N+1;D=T:sub(N,N)else local L=S(N/O)local U=N%O;D=T:sub(U,U)while L>0 do U=L%I;D=
  587. A:sub(U,U)..D;L=S(L/I)end;N=N+1 end end until not(x[D]or self:GetVariable(D))self:RenameLocal(R.Name,D)end end;function z:ToString()return'<Scope>'end
  588. local function _(E)
  589. local T=setmetatable({Parent=E,Locals={},Globals={},oldLocalNamesMap={},oldGlobalNamesMap={},Children={}},{__index=z})if E then table.insert(E.Children,T)end;return T end;return _ end)
  590. local m=e(function(j,...)local x={}function x:Peek(z)local _=self.tokens;z=z or 0;return
  591. _[math.min(#_,self.pointer+z)]end
  592. function x:Get(z)
  593. local _=self.tokens;local E=self.pointer;local T=_[E]self.pointer=math.min(E+1,#_)if z then
  594. table.insert(z,T)end;return T end;function x:Is(z)return self:Peek().Type==z end;function x:Save()
  595. table.insert(self.savedPointers,self.pointer)end
  596. function x:Commit()local z=self.savedPointers;z[#z]=nil end
  597. function x:Restore()local z=self.savedPointers;local _=#z;self.pointer=savedP[_]z[_]=nil end
  598. function x:ConsumeSymbol(z,_)local E=self:Peek()
  599. if E.Type=='Symbol'then
  600. if z then if E.Data==z then self:Get(_)
  601. return true else return nil end else self:Get(_)return E end else return nil end end
  602. function x:ConsumeKeyword(z,_)local E=self:Peek()if E.Type=='Keyword'and E.Data==z then
  603. self:Get(_)return true else return nil end end;function x:IsKeyword(z)local _=self:Peek()
  604. return _.Type=='Keyword'and _.Data==z end
  605. function x:IsSymbol(z)local _=self:Peek()return _.Type==
  606. 'Symbol'and _.Data==z end
  607. function x:IsEof()return self:Peek().Type=='Eof'end
  608. function x:Print(z)z=(z==nil and true or z)local _=""
  609. for E,T in
  610. ipairs(self.tokens)do if z then
  611. for E,A in ipairs(T.LeadingWhite)do _=_..A:Print().."\n"end end;_=_..T:Print().."\n"end;return _ end;return x end)
  612. local f=e(function(j,...)local x=o.CreateLookup;local z=u.LowerChars;local _=u.UpperChars;local E=u.Digits
  613. local T=u.Symbols;local A=u.HexDigits;local O=u.Keywords;local I=u.StatListCloseKeywords;local N=u.UnOps
  614. local S=setmetatable;local H={}
  615. function H:Print()return
  616. "<".. (self.Type..
  617. string.rep(' ',math.max(3,12-#self.Type))).."  "..
  618. (self.Data or'').." >"end;local R={__index=H}
  619. local function D(U)local C={}
  620. do local F=1;local W=1;local Y=1;local function P()local Q=U:sub(F,F)
  621. if Q=='\n'then Y=1;W=W+1 else Y=Y+1 end;F=F+1;return Q end;local function V(Q)Q=Q or 0;return
  622. U:sub(F+Q,F+Q)end;local function B(Q)local J=V()for X=1,#Q do
  623. if J==Q:sub(X,X)then return P()end end end;local function G(Q)
  624. error(">> :"..W..
  625. ":"..Y..": "..Q,0)end
  626. local function K()local Q=F
  627. if V()=='['then local J=0;local X=1
  628. while V(J+1)=='='do J=J+1 end
  629. if V(J+1)=='['then for ea=0,J+1 do P()end;local Z=F
  630. while true do if V()==''then
  631. G("Expected `]"..string.rep('=',J)..
  632. "]` near <eof>.",3)end;local ea=true
  633. if V()==']'then for eo=1,J do
  634. if V(eo)~='='then ea=false end end;if V(J+1)~=']'then ea=false end else
  635. if V()=='['then
  636. local eo=true;for ei=1,J do if V(ei)~='='then eo=false;break end end;if
  637. V(J+1)=='['and eo then X=X+1;for ei=1,(J+2)do P()end end end;ea=false end
  638. if ea then X=X-1;if X==0 then break else for eo=1,J+2 do P()end end else P()end end;local ee=U:sub(Z,F-1)for ea=0,J+1 do P()end;local et=U:sub(Q,F-1)return ee,et else
  639. return nil end else return nil end end
  640. while true do local Q={}local J=''local X=false
  641. while true do local ei=V()
  642. if ei=='#'and V(1)=='!'and W==1 then
  643. P()P()J="#!"while V()~='\n'and V()~=''do J=J..P()end
  644. table.insert(Q,S({Type='Comment',CommentType='Shebang',Data=J,Line=W,Char=Y},R))J=""end
  645. if ei==' 'or ei=='\t'then local en=P()
  646. table.insert(Q,S({Type='Whitespace',Line=W,Char=Y,Data=en},R))elseif ei=='\n'or ei=='\r'then local en=P()if J~=""then
  647. table.insert(Q,S({Type='Comment',CommentType=X and'LongComment'or'Comment',Data=J,Line=W,Char=Y},R))J=""end
  648. table.insert(Q,S({Type='Whitespace',Line=W,Char=Y,Data=en},R))elseif ei=='-'and V(1)=='-'then P()P()J=J..'--'local en,es=K()
  649. if es then J=J..es;X=true else while
  650. V()~='\n'and V()~=''do J=J..P()end end else break end end;if J~=""then
  651. table.insert(Q,S({Type='Comment',CommentType=X and'LongComment'or'Comment',Data=J,Line=W,Char=Y},R))end;local Z=W;local ee=Y;local et=":"..W..":"..
  652. Y..":> "local ea=V()local eo=nil
  653. if ea==''then
  654. eo={Type='Eof'}elseif _[ea]or z[ea]or ea=='_'then local ei=F
  655. repeat P()ea=V()until not(
  656. _[ea]or z[ea]or E[ea]or ea=='_')local en=U:sub(ei,F-1)if O[en]then eo={Type='Keyword',Data=en}else
  657. eo={Type='Ident',Data=en}end elseif
  658. E[ea]or(V()=='.'and E[V(1)])then local ei=F
  659. if ea=='0'and V(1)=='x'then P()P()while A[V()]do P()end;if B('Pp')then B('+-')while
  660. E[V()]do P()end end else while E[V()]do P()end;if B('.')then
  661. while E[V()]do P()end end
  662. if B('Ee')then B('+-')while E[V()]do P()end end end;eo={Type='Number',Data=U:sub(ei,F-1)}elseif
  663. ea=='\''or ea=='\"'then local ei=F;local en=P()local es=F
  664. while true do local ea=P()if ea=='\\'then P()elseif ea==en then break elseif ea==''then
  665. G("Unfinished string near <eof>")end end;local eh=U:sub(es,F-2)local er=U:sub(ei,F-1)
  666. eo={Type='String',Data=er,Constant=eh}elseif ea=='['then local ei,en=K()if en then eo={Type='String',Data=en,Constant=ei}else P()
  667. eo={Type='Symbol',Data='['}end elseif B('>=<')then if B('=')then
  668. eo={Type='Symbol',Data=ea..'='}else eo={Type='Symbol',Data=ea}end elseif B('~')then if
  669. B('=')then eo={Type='Symbol',Data='~='}else
  670. G("Unexpected symbol `~` in source.",2)end elseif B('.')then if B('.')then if B('.')then
  671. eo={Type='Symbol',Data='...'}else eo={Type='Symbol',Data='..'}end else
  672. eo={Type='Symbol',Data='.'}end elseif B(':')then if B(':')then
  673. eo={Type='Symbol',Data='::'}else eo={Type='Symbol',Data=':'}end elseif T[ea]then
  674. P()eo={Type='Symbol',Data=ea}else local ei,en=K()if ei then
  675. eo={Type='String',Data=en,Constant=ei}else
  676. G("Unexpected Symbol `"..ea.."` in source.",2)end end;eo.LeadingWhite=Q;eo.Line=Z;eo.Char=ee;C[#C+1]=S(eo,R)
  677. if eo.Type=='Eof'then break end end end
  678. local M=setmetatable({tokens=C,savedPointers={},pointer=1},{__index=m})return M end
  679. local function L(U)
  680. local function C(Q)
  681. local J=">> :"..U:Peek().Line..
  682. ":"..U:Peek().Char..": "..Q.."\n"local X=0
  683. if type(src)=='string'then
  684. for Z in src:gmatch("[^\n]*\n?")do if Z:sub(-1,-1)=='\n'then Z=Z:sub(1,
  685. -2)end;X=X+1
  686. if X==U:Peek().Line then J=J..">> `"..
  687. Z:gsub('\t','    ').."`\n"
  688. for ee=1,U:Peek().Char
  689. do local et=Z:sub(ee,ee)if et=='\t'then J=J..'    'else J=J..' 'end end;J=J.."   ^^^^"break end end end;error(J)end;local M,F,W,Y,P
  690. local function V(Q,J)local X=c(Q)if not U:ConsumeSymbol('(',J)then
  691. C("`(` expected.")end;local Z={}local ee=false
  692. while
  693. not U:ConsumeSymbol(')',J)do
  694. if U:Is('Ident')then local ea=X:CreateLocal(U:Get(J).Data)
  695. Z[#Z+1]=ea
  696. if not U:ConsumeSymbol(',',J)then if U:ConsumeSymbol(')',J)then break else
  697. C("`)` expected.")end end elseif U:ConsumeSymbol('...',J)then ee=true;if not U:ConsumeSymbol(')',J)then
  698. C("`...` must be the last argument of a function.")end;break else
  699. C("Argument name or `...` expected")end end;local et=F(X)if not U:ConsumeKeyword('end',J)then
  700. C("`end` expected after function body")end;return
  701. {AstType='Function',Scope=X,Arguments=Z,Body=et,VarArg=ee,Tokens=J}end
  702. function Y(Q)local J={}
  703. if U:ConsumeSymbol('(',J)then local X=M(Q)if not U:ConsumeSymbol(')',J)then
  704. C("`)` Expected.")end
  705. return{AstType='Parentheses',Inner=X,Tokens=J}elseif U:Is('Ident')then local X=U:Get(J)local Z=Q:GetLocal(X.Data)
  706. if not Z then
  707. Z=Q:GetGlobal(X.Data)
  708. if not Z then Z=Q:CreateGlobal(X.Data)else Z.References=Z.References+1 end else Z.References=Z.References+1 end;return{AstType='VarExpr',Name=X.Data,Variable=Z,Tokens=J}else
  709. C("primary expression expected")end end
  710. function P(Q,J)local X=Y(Q)
  711. while true do local Z={}
  712. if U:IsSymbol('.')or U:IsSymbol(':')then
  713. local ee=U:Get(Z).Data
  714. if not U:Is('Ident')then C("<Ident> expected.")end;local et=U:Get(Z)
  715. X={AstType='MemberExpr',Base=X,Indexer=ee,Ident=et,Tokens=Z}elseif not J and U:ConsumeSymbol('[',Z)then local ee=M(Q)if
  716. not U:ConsumeSymbol(']',Z)then C("`]` expected.")end
  717. X={AstType='IndexExpr',Base=X,Index=ee,Tokens=Z}elseif not J and U:ConsumeSymbol('(',Z)then local ee={}
  718. while
  719. not U:ConsumeSymbol(')',Z)do ee[#ee+1]=M(Q)
  720. if not U:ConsumeSymbol(',',Z)then if
  721. U:ConsumeSymbol(')',Z)then break else C("`)` Expected.")end end end;X={AstType='CallExpr',Base=X,Arguments=ee,Tokens=Z}elseif
  722. not J and U:Is('String')then
  723. X={AstType='StringCallExpr',Base=X,Arguments={U:Get(Z)},Tokens=Z}elseif not J and U:IsSymbol('{')then local ee=W(Q)
  724. X={AstType='TableCallExpr',Base=X,Arguments={ee},Tokens=Z}else break end end;return X end
  725. function W(Q)local J={}
  726. if U:Is('Number')then
  727. return{AstType='NumberExpr',Value=U:Get(J),Tokens=J}elseif U:Is('String')then
  728. return{AstType='StringExpr',Value=U:Get(J),Tokens=J}elseif U:ConsumeKeyword('nil',J)then return{AstType='NilExpr',Tokens=J}elseif
  729. U:IsKeyword('false')or U:IsKeyword('true')then return
  730. {AstType='BooleanExpr',Value=(U:Get(J).Data=='true'),Tokens=J}elseif U:ConsumeSymbol('...',J)then return
  731. {AstType='DotsExpr',Tokens=J}elseif U:ConsumeSymbol('{',J)then local X={}
  732. local Z={AstType='ConstructorExpr',EntryList=X,Tokens=J}
  733. while true do
  734. if U:IsSymbol('[',J)then U:Get(J)local ee=M(Q)if not U:ConsumeSymbol(']',J)then
  735. C("`]` Expected")end;if not U:ConsumeSymbol('=',J)then
  736. C("`=` Expected")end;local et=M(Q)
  737. X[#X+1]={Type='Key',Key=ee,Value=et}elseif U:Is('Ident')then local ee=U:Peek(1)
  738. if
  739. ee.Type=='Symbol'and ee.Data=='='then local et=U:Get(J)
  740. if not U:ConsumeSymbol('=',J)then C("`=` Expected")end;local ea=M(Q)X[#X+1]={Type='KeyString',Key=et.Data,Value=ea}else
  741. local et=M(Q)X[#X+1]={Type='Value',Value=et}end elseif U:ConsumeSymbol('}',J)then break else local ee=M(Q)X[#X+1]={Type='Value',Value=ee}end
  742. if U:ConsumeSymbol(';',J)or U:ConsumeSymbol(',',J)then elseif
  743. U:ConsumeSymbol('}',J)then break else C("`}` or table entry Expected")end end;return Z elseif U:ConsumeKeyword('function',J)then local X=V(Q,J)X.IsLocal=true;return X else return P(Q)end end;local B=8
  744. local G={['+']={6,6},['-']={6,6},['%']={7,7},['/']={7,7},['*']={7,7},['^']={10,9},['..']={5,4},['==']={3,3},['<']={3,3},['<=']={3,3},['~=']={3,3},['>']={3,3},['>=']={3,3},['and']={2,2},['or']={1,1}}
  745. function M(Q,J)J=J or 0;local X
  746. if N[U:Peek().Data]then local Z={}local ee=U:Get(Z).Data;X=M(Q,B)
  747. local et={AstType='UnopExpr',Rhs=X,Op=ee,OperatorPrecedence=B,Tokens=Z}X=et else X=W(Q)end
  748. while true do local Z=G[U:Peek().Data]
  749. if Z and Z[1]>J then local ee={}
  750. local et=U:Get(ee).Data;local ea=M(Q,Z[2])
  751. local eo={AstType='BinopExpr',Lhs=X,Op=et,OperatorPrecedence=Z[1],Rhs=ea,Tokens=ee}X=eo else break end end;return X end
  752. local function K(Q)local J=nil;local X={}
  753. if U:ConsumeKeyword('if',X)then local Z={}
  754. local ee={AstType='IfStatement',Clauses=Z}
  755. repeat local et=M(Q)if not U:ConsumeKeyword('then',X)then
  756. C("`then` expected.")end;local ea=F(Q)
  757. Z[#Z+1]={Condition=et,Body=ea}until not U:ConsumeKeyword('elseif',X)
  758. if U:ConsumeKeyword('else',X)then local et=F(Q)Z[#Z+1]={Body=et}end
  759. if not U:ConsumeKeyword('end',X)then C("`end` expected.")end;ee.Tokens=X;J=ee elseif U:ConsumeKeyword('while',X)then local Z=M(Q)if
  760. not U:ConsumeKeyword('do',X)then return C("`do` expected.")end;local ee=F(Q)if not
  761. U:ConsumeKeyword('end',X)then C("`end` expected.")end
  762. J={AstType='WhileStatement',Condition=Z,Body=ee,Tokens=X}elseif U:ConsumeKeyword('do',X)then local Z=F(Q)if not U:ConsumeKeyword('end',X)then
  763. C("`end` expected.")end
  764. J={AstType='DoStatement',Body=Z,Tokens=X}elseif U:ConsumeKeyword('for',X)then if not U:Is('Ident')then
  765. C("<ident> expected.")end;local Z=U:Get(X)
  766. if U:ConsumeSymbol('=',X)then local ee=c(Q)
  767. local et=ee:CreateLocal(Z.Data)local ea=M(Q)
  768. if not U:ConsumeSymbol(',',X)then C("`,` Expected")end;local eo=M(Q)local ei;if U:ConsumeSymbol(',',X)then ei=M(Q)end;if not
  769. U:ConsumeKeyword('do',X)then C("`do` expected")end
  770. local en=F(ee)
  771. if not U:ConsumeKeyword('end',X)then C("`end` expected")end
  772. J={AstType='NumericForStatement',Scope=ee,Variable=et,Start=ea,End=eo,Step=ei,Body=en,Tokens=X}else local ee=c(Q)local et={ee:CreateLocal(Z.Data)}
  773. while
  774. U:ConsumeSymbol(',',X)do
  775. if not U:Is('Ident')then C("for variable expected.")end;et[#et+1]=ee:CreateLocal(U:Get(X).Data)end
  776. if not U:ConsumeKeyword('in',X)then C("`in` expected.")end;local ea={M(Q)}
  777. while U:ConsumeSymbol(',',X)do ea[#ea+1]=M(Q)end
  778. if not U:ConsumeKeyword('do',X)then C("`do` expected.")end;local eo=F(ee)if not U:ConsumeKeyword('end',X)then
  779. C("`end` expected.")end
  780. J={AstType='GenericForStatement',Scope=ee,VariableList=et,Generators=ea,Body=eo,Tokens=X}end elseif U:ConsumeKeyword('repeat',X)then local Z=F(Q)if
  781. not U:ConsumeKeyword('until',X)then C("`until` expected.")end
  782. local ee=M(Z.Scope)J={AstType='RepeatStatement',Condition=ee,Body=Z,Tokens=X}elseif
  783. U:ConsumeKeyword('function',X)then
  784. if not U:Is('Ident')then C("Function name expected")end;local Z=P(Q,true)local ee=V(Q,X)ee.IsLocal=false;ee.Name=Z;J=ee elseif
  785. U:ConsumeKeyword('local',X)then
  786. if U:Is('Ident')then local Z={U:Get(X).Data}
  787. while U:ConsumeSymbol(',',X)do
  788. if not
  789. U:Is('Ident')then C("local var name expected")end;Z[#Z+1]=U:Get(X).Data end;local ee={}if U:ConsumeSymbol('=',X)then repeat ee[#ee+1]=M(Q)until
  790. not U:ConsumeSymbol(',',X)end;for et,ea in
  791. pairs(Z)do Z[et]=Q:CreateLocal(ea)end
  792. J={AstType='LocalStatement',LocalList=Z,InitList=ee,Tokens=X}elseif U:ConsumeKeyword('function',X)then if not U:Is('Ident')then
  793. C("Function name expected")end;local Z=U:Get(X).Data
  794. local ee=Q:CreateLocal(Z)local et=V(Q,X)et.Name=ee;et.IsLocal=true;J=et else
  795. C("local var or function def expected")end elseif U:ConsumeSymbol('::',X)then if not U:Is('Ident')then
  796. C('Label name expected')end;local Z=U:Get(X).Data;if
  797. not U:ConsumeSymbol('::',X)then C("`::` expected")end
  798. J={AstType='LabelStatement',Label=Z,Tokens=X}elseif U:ConsumeKeyword('return',X)then local Z={}if not U:IsKeyword('end')then
  799. local ee,et=pcall(function()return M(Q)end)
  800. if ee then Z[1]=et;while U:ConsumeSymbol(',',X)do Z[#Z+1]=M(Q)end end end
  801. J={AstType='ReturnStatement',Arguments=Z,Tokens=X}elseif U:ConsumeKeyword('break',X)then J={AstType='BreakStatement',Tokens=X}elseif
  802. U:ConsumeKeyword('goto',X)then if not U:Is('Ident')then C("Label expected")end
  803. local Z=U:Get(X).Data;J={AstType='GotoStatement',Label=Z,Tokens=X}else local Z=P(Q)
  804. if
  805. U:IsSymbol(',')or U:IsSymbol('=')then if(Z.ParenCount or 0)>0 then
  806. C("Can not assign to parenthesized expression, is not an lvalue")end;local ee={Z}while
  807. U:ConsumeSymbol(',',X)do ee[#ee+1]=P(Q)end;if
  808. not U:ConsumeSymbol('=',X)then C("`=` Expected.")end;local et={M(Q)}while
  809. U:ConsumeSymbol(',',X)do et[#et+1]=M(Q)end
  810. J={AstType='AssignmentStatement',Lhs=ee,Rhs=et,Tokens=X}elseif Z.AstType=='CallExpr'or Z.AstType=='TableCallExpr'or
  811. Z.AstType=='StringCallExpr'then
  812. J={AstType='CallStatement',Expression=Z,Tokens=X}else C("Assignment Statement Expected")end end
  813. if U:IsSymbol(';')then J.Semicolon=U:Get(J.Tokens)end;return J end
  814. function F(Q)local J={}local X={Scope=c(Q),AstType='Statlist',Body=J,Tokens={}}
  815. while not
  816. I[U:Peek().Data]and not U:IsEof()do local Z=K(X.Scope)J[#J+1]=Z end
  817. if U:IsEof()then local Z={}Z.AstType='Eof'Z.Tokens={U:Get()}J[#J+1]=Z end;return X end;return F(c())end;return{LexLua=D,ParseLua=L}end)
  818. local w=e(function(j,...)local x=u.LowerChars;local z=u.UpperChars;local _=u.Digits;local E=u.Symbols
  819. local function T(N,S,H)H=H or' '
  820. local R,D=N:sub(-1,-1),S:sub(1,1)
  821. if z[R]or x[R]or R=='_'then
  822. if not
  823. (D=='_'or z[D]or x[D]or _[D])then return N..S else return N..H..S end elseif _[R]then
  824. if D=='('then return N..S elseif E[D]then return N..S else return N..H..S end elseif R==''then return N..S else if D=='('then return N..H..S else return N..S end end end
  825. local function A(N)local S,H;local R=0;local function D(U,C,M)
  826. if R>150 then R=0;return U.."\n"..C else return T(U,C,M)end end
  827. H=function(U,C)local C=C or 0;local M=0;local F=false;local W=""
  828. if
  829. U.AstType=='VarExpr'then
  830. if U.Variable then W=W..U.Variable.Name else W=W..U.Name end elseif U.AstType=='NumberExpr'then W=W..U.Value.Data elseif U.AstType=='StringExpr'then W=W..
  831. U.Value.Data elseif U.AstType=='BooleanExpr'then
  832. W=W..tostring(U.Value)elseif U.AstType=='NilExpr'then W=D(W,"nil")elseif U.AstType=='BinopExpr'then
  833. M=U.OperatorPrecedence;W=D(W,H(U.Lhs,M))W=D(W,U.Op)W=D(W,H(U.Rhs))if U.Op=='^'or U.Op==
  834. '..'then M=M-1 end;if M<C then F=false else F=true end elseif
  835. U.AstType=='UnopExpr'then W=D(W,U.Op)W=D(W,H(U.Rhs))elseif U.AstType=='DotsExpr'then W=W..
  836. "..."elseif U.AstType=='CallExpr'then W=W..H(U.Base)W=W.."("for Y=1,#U.Arguments do W=
  837. W..H(U.Arguments[Y])
  838. if Y~=#U.Arguments then W=W..","end end;W=W..")"elseif
  839. U.AstType=='TableCallExpr'then W=W..H(U.Base)W=W..H(U.Arguments[1])elseif U.AstType==
  840. 'StringCallExpr'then W=W..H(U.Base)
  841. W=W..U.Arguments[1].Data elseif U.AstType=='IndexExpr'then W=W..
  842. H(U.Base).."["..H(U.Index).."]"elseif U.AstType=='MemberExpr'then W=W..H(U.Base)..U.Indexer..
  843. U.Ident.Data elseif U.AstType==
  844. 'Function'then U.Scope:ObfuscateLocals()W=W.."function("
  845. if#
  846. U.Arguments>0 then for Y=1,#U.Arguments do W=W..U.Arguments[Y].Name
  847. if Y~=#
  848. U.Arguments then W=W..","elseif U.VarArg then W=W..",..."end end elseif U.VarArg then
  849. W=W.."..."end;W=W..")"W=D(W,S(U.Body))W=D(W,"end")elseif
  850. U.AstType=='ConstructorExpr'then W=W.."{"
  851. for Y=1,#U.EntryList do local P=U.EntryList[Y]
  852. if P.Type=='Key'then W=W.."["..H(P.Key).."]="..
  853. H(P.Value)elseif
  854. P.Type=='Value'then W=W..H(P.Value)elseif P.Type=='KeyString'then W=W..P.Key..
  855. "="..H(P.Value)end;if Y~=#U.EntryList then W=W..","end end;W=W.."}"elseif U.AstType=='Parentheses'then
  856. W=W.."("..H(U.Inner)..")"end;if not F then
  857. W=string.rep('(',U.ParenCount or 0)..W
  858. W=W..string.rep(')',U.ParenCount or 0)end;R=R+#W;return W end
  859. local L=function(U)local C=''
  860. if U.AstType=='AssignmentStatement'then for M=1,#U.Lhs do C=C..H(U.Lhs[M])if M~=#
  861. U.Lhs then C=C..","end end
  862. if
  863. #U.Rhs>0 then C=C.."="for M=1,#U.Rhs do C=C..H(U.Rhs[M])
  864. if M~=#U.Rhs then C=C..","end end end elseif U.AstType=='CallStatement'then C=H(U.Expression)elseif
  865. U.AstType=='LocalStatement'then C=C.."local "
  866. for M=1,#U.LocalList do
  867. C=C..U.LocalList[M].Name;if M~=#U.LocalList then C=C..","end end;if#U.InitList>0 then C=C.."="
  868. for M=1,#U.InitList do
  869. C=C..H(U.InitList[M])if M~=#U.InitList then C=C..","end end end elseif
  870. U.AstType=='IfStatement'then C=D("if",H(U.Clauses[1].Condition))
  871. C=D(C,"then")C=D(C,S(U.Clauses[1].Body))
  872. for M=2,#U.Clauses do
  873. local F=U.Clauses[M]if F.Condition then C=D(C,"elseif")C=D(C,H(F.Condition))
  874. C=D(C,"then")else C=D(C,"else")end
  875. C=D(C,S(F.Body))end;C=D(C,"end")elseif U.AstType=='WhileStatement'then
  876. C=D("while",H(U.Condition))C=D(C,"do")C=D(C,S(U.Body))C=D(C,"end")elseif
  877. U.AstType=='DoStatement'then C=D(C,"do")C=D(C,S(U.Body))C=D(C,"end")elseif
  878. U.AstType=='ReturnStatement'then C="return"
  879. for M=1,#U.Arguments do C=D(C,H(U.Arguments[M]))if M~=#
  880. U.Arguments then C=C..","end end elseif U.AstType=='BreakStatement'then C="break"elseif U.AstType=='RepeatStatement'then C="repeat"
  881. C=D(C,S(U.Body))C=D(C,"until")C=D(C,H(U.Condition))elseif U.AstType=='Function'then
  882. U.Scope:ObfuscateLocals()if U.IsLocal then C="local"end;C=D(C,"function ")if U.IsLocal then
  883. C=C..U.Name.Name else C=C..H(U.Name)end;C=C.."("
  884. if
  885. #U.Arguments>0 then
  886. for M=1,#U.Arguments do C=C..U.Arguments[M].Name;if
  887. M~=#U.Arguments then C=C..","elseif U.VarArg then C=C..",..."end end elseif U.VarArg then C=C.."..."end;C=C..")"C=D(C,S(U.Body))C=D(C,"end")elseif
  888. U.AstType=='GenericForStatement'then U.Scope:ObfuscateLocals()C="for "for M=1,#U.VariableList do C=C..
  889. U.VariableList[M].Name
  890. if M~=#U.VariableList then C=C..","end end;C=C.." in"
  891. for M=1,#U.Generators do
  892. C=D(C,H(U.Generators[M]))if M~=#U.Generators then C=D(C,',')end end;C=D(C,"do")C=D(C,S(U.Body))C=D(C,"end")elseif
  893. U.AstType=='NumericForStatement'then U.Scope:ObfuscateLocals()C="for "
  894. C=C..U.Variable.Name.."="C=C..H(U.Start)..","..H(U.End)if U.Step then C=C..","..
  895. H(U.Step)end;C=D(C,"do")
  896. C=D(C,S(U.Body))C=D(C,"end")elseif U.AstType=='LabelStatement'then
  897. C="::"..U.Label.."::"elseif U.AstType=='GotoStatement'then C="goto "..U.Label elseif U.AstType=='Comment'then elseif U.AstType==
  898. 'Eof'then else
  899. error("Unknown AST Type: "..U.AstType)end;R=R+#C;return C end
  900. S=function(U)local C=''U.Scope:ObfuscateLocals()for M,F in pairs(U.Body)do
  901. C=D(C,L(F),';')end;return C end;return S(N)end;local function O(N)local S=f.LexLua(N)a.refreshYield()S=f.ParseLua(S)
  902. a.refreshYield()return A(S)end
  903. local function I(N,S,H)H=H or S
  904. local R=fs.open(fs.combine(N,S),"r")local D=R.readAll()R.close()D=O(D)
  905. local L=fs.open(fs.combine(N,H),"w")L.write(D)L.close()end;return{JoinStatements=T,Minify=A,MinifyString=O,MinifyFile=I}end)
  906. do local j=w.MinifyFile
  907. local x=function(z,_,E,T)return j(_.CurrentDirectory,E,T)end
  908. function r.Runner:Minify(z,_,E,T)
  909. return
  910. self:AddTask(z,T,function(A,O)
  911. if type(_)=="table"then
  912. assert(type(E)=="table","Output File must be a table too")local I=#_
  913. assert(I==#E,"Tables must be the same length")for N=1,I do j(O.CurrentDirectory,_[N],E[N])end else
  914. j(O.CurrentDirectory,_,E)end end):Description(
  915. "Minifies '"..
  916. fs.getName(_).."' into '"..fs.getName(E).."'"):Requires(_):Produces(E)end
  917. function r.Runner:MinifyAll(z,_,E)z=z or"_minify"return
  918. self:AddTask(z,{},x):Description("Minifies files"):Maps(
  919. _ or"wild:*.lua",E or"wild:*.min.lua")end
  920. i.Subscribe({"HowlFile","env"},function(z)z.Minify=j end)end
  921. do local j,x,z,_,E=fs.combine,fs.exists,fs.isDir,loadfile,o.Verbose;local T=busted
  922. local A={"busted.api.lua","../lib/busted.api.lua","busted.api","../lib/busted.api","busted","../lib/busted"}
  923. local function O(H)E("Busted at "..H)local R=_(H)
  924. if R then
  925. E("Busted loading at "..H)local D=setfenv(R,getfenv())()D=D.api or D;if D.run then E(
  926. "Busted found at "..H)return D end end end
  927. local function I(H)if not x(H)then return end;if not z(H)then return O(H)end;local R
  928. for D,L in ipairs(A)do R=j(H,L)if
  929. x(R)then local U=O(R)if U then return U end end end end
  930. local function N()if T then return T end;local H=I("/")if H then T=H;return T end;for R in
  931. string.gmatch(shell.path(),"[^:]+")do local H=I(R)if H then T=H;return T end end end
  932. local function S(H)
  933. return
  934. {cwd=H,output='colorTerminal',seed=os.time(),verbose=o.IsVerbose(),root='spec',tags={},['exclude-tags']={},pattern='_spec',loaders={'lua'},helper=''}end
  935. function r.Runner:Busted(H,R,D)
  936. return
  937. self:AddTask(H,D,function(L,U)local T
  938. if R and R.busted then T=I(R.busted)else T=N()end;if not T then error("Cannot find busted")end
  939. local C=S(U.CurrentDirectory)for W,Y in pairs(R or{})do C[W]=Y end
  940. local M,F=T.run(C,S(U.CurrentDirectory))
  941. if M~=0 then o.VerboseLog(F)error("Not all tests passed")end end):Description("Runs tests")end end
  942. local y=e(function(j,...)local x={}
  943. function x:Add(_)
  944. if type(_)=="table"then for E,T in ipairs(_)do self:Add(T)end else
  945. table.insert(self.include,self:_Parse(_))self.files=nil end;return self end
  946. function x:Remove(_)
  947. if type(_)=="table"then for E,T in ipairs(_)do self:Remove(T)end else
  948. table.insert(self.exclude,self:_Parse(_))self.files=nil end;return self end;x.Include=x.Add;x.Exclude=x.Remove
  949. function x:Startup(_)self.startup=_;return self end
  950. function x:Files()
  951. if not self.files then self.files={}
  952. for E,T in ipairs(self.include)do if T.Type=="Normal"then
  953. self:_Include(T.Text)else self:_Include("",T)end end end;return self.files end
  954. function x:_Include(_,E)if _~=""then
  955. for A,E in pairs(self.exclude)do if E.Match(_)then return end end end
  956. local T=fs.combine(self.path,_)
  957. assert(fs.exists(T),"Cannot find path ".._)if fs.isDir(T)then for A,O in ipairs(fs.list(T))do
  958. self:_Include(fs.combine(_,O),E)end elseif not E or E.Match(_)then
  959. self.files[_]=true end end
  960. function x:_Parse(_)_=o.ParsePattern(_)local E=_.Text
  961. if _.Type=="Normal"then
  962. function _.Match(T)return E==T end else function _.Match(T)return T:match(E)end end;return _ end
  963. local function z(_)return
  964. setmetatable({path=_,include={},exclude={},startup='startup'},{__index=x}):Remove{".git",".idea","Howlfile.lua","Howlfile","build"}end
  965. i.Subscribe({"HowlFile","env"},function(_)_.Files=function(E)
  966. return z(E or _.CurrentDirectory)end end)return{Files=x,Factory=z}end)
  967. do
  968. local j=[=[--[[Hideously Smashed Together by Compilr, a Hideous Smash-Stuff-Togetherer, (c) 2014 oeed
  969.     This file REALLLLLLLY isn't suitable to be used for anything other than being executed
  970.     To extract all the files, run: "<filename> --extract" in the Shell
  971. ]]
  972. ]=]
  973. local x=[[
  974. local function run(tArgs)
  975.     local fnFile, err = loadstring(files[%q], %q)
  976.     if err then error(err) end
  977.  
  978.     local function split(str, pat)
  979.          local t = {}
  980.          local fpat = "(.-)" .. pat
  981.          local last_end = 1
  982.          local s, e, cap = str:find(fpat, 1)
  983.          while s do
  984.                 if s ~= 1 or cap ~= "" then
  985.          table.insert(t,cap)
  986.                 end
  987.                 last_end = e+1
  988.                 s, e, cap = str:find(fpat, last_end)
  989.          end
  990.          if last_end <= #str then
  991.                 cap = str:sub(last_end)
  992.                 table.insert(t, cap)
  993.          end
  994.          return t
  995.     end
  996.  
  997.     local function resolveTreeForPath(path, single)
  998.         local _files = files
  999.         local parts = split(path, '/')
  1000.         if parts then
  1001.             for i, v in ipairs(parts) do
  1002.                 if #v > 0 then
  1003.                     if _files[v] then
  1004.                         _files = _files[v]
  1005.                     else
  1006.                         _files = nil
  1007.                         break
  1008.                     end
  1009.                 end
  1010.             end
  1011.         elseif #path > 0 and path ~= '/' then
  1012.             _files = _files[path]
  1013.         end
  1014.         if not single or type(_files) == 'string' then
  1015.             return _files
  1016.         end
  1017.     end
  1018.  
  1019.     local oldFs = fs
  1020.     local env
  1021.     env = {
  1022.         fs = {
  1023.             list = function(path)
  1024.                             local list = {}
  1025.                             if fs.exists(path) then
  1026.                         list = fs.list(path)
  1027.                             end
  1028.                 for k, v in pairs(resolveTreeForPath(path)) do
  1029.                     if not fs.exists(path .. '/' ..k) then
  1030.                         table.insert(list, k)
  1031.                     end
  1032.                 end
  1033.                 return list
  1034.             end,
  1035.  
  1036.             exists = function(path)
  1037.                 if fs.exists(path) then
  1038.                     return true
  1039.                 elseif resolveTreeForPath(path) then
  1040.                     return true
  1041.                 else
  1042.                     return false
  1043.                 end
  1044.             end,
  1045.  
  1046.             isDir = function(path)
  1047.                 if fs.isDir(path) then
  1048.                     return true
  1049.                 else
  1050.                     local tree = resolveTreeForPath(path)
  1051.                     if tree and type(tree) == 'table' then
  1052.                         return true
  1053.                     else
  1054.                         return false
  1055.                     end
  1056.                 end
  1057.             end,
  1058.  
  1059.             isReadOnly = function(path)
  1060.                 if not fs.isReadOnly(path) then
  1061.                     return false
  1062.                 else
  1063.                     return true
  1064.                 end
  1065.             end,
  1066.  
  1067.             getName = fs.getName,
  1068.             getSize = fs.getSize,
  1069.             getFreespace = fs.getFreespace,
  1070.             makeDir = fs.makeDir,
  1071.             move = fs.move,
  1072.             copy = fs.copy,
  1073.             delete = fs.delete,
  1074.             combine = fs.combine,
  1075.  
  1076.             open = function(path, mode)
  1077.                 if fs.exists(path) then
  1078.                     return fs.open(path, mode)
  1079.                 elseif type(resolveTreeForPath(path)) == 'string' then
  1080.                     local handle = {close = function()end}
  1081.                     if mode == 'r' then
  1082.                         local content = resolveTreeForPath(path)
  1083.                         handle.readAll = function()
  1084.                             return content
  1085.                         end
  1086.  
  1087.                         local line = 1
  1088.                         local lines = split(content, '\n')
  1089.                         handle.readLine = function()
  1090.                             if line > #lines then
  1091.                                 return nil
  1092.                             else
  1093.                                 return lines[line]
  1094.                             end
  1095.                             line = line + 1
  1096.                         end
  1097.                                             return handle
  1098.                     else
  1099.                         error('Cannot write to read-only file (compilr archived).')
  1100.                     end
  1101.                 else
  1102.                     return fs.open(path, mode)
  1103.                 end
  1104.             end
  1105.         },
  1106.  
  1107.         loadfile = function( _sFile )
  1108.                 local file = env.fs.open( _sFile, "r" )
  1109.                 if file then
  1110.                         local func, err = loadstring( file.readAll(), fs.getName( _sFile ) )
  1111.                         file.close()
  1112.                         return func, err
  1113.                 end
  1114.                 return nil, "File not found: ".._sFile
  1115.         end,
  1116.  
  1117.         dofile = function( _sFile )
  1118.                 local fnFile, e = env.loadfile( _sFile )
  1119.                 if fnFile then
  1120.                         setfenv( fnFile, getfenv(2) )
  1121.                         return fnFile()
  1122.                 else
  1123.                         error( e, 2 )
  1124.                 end
  1125.         end
  1126.     }
  1127.  
  1128.     setmetatable( env, { __index = _G } )
  1129.  
  1130.     local tAPIsLoading = {}
  1131.     env.os.loadAPI = function( _sPath )
  1132.             local sName = fs.getName( _sPath )
  1133.             if tAPIsLoading[sName] == true then
  1134.                     printError( "API "..sName.." is already being loaded" )
  1135.                     return false
  1136.             end
  1137.             tAPIsLoading[sName] = true
  1138.  
  1139.             local tEnv = {}
  1140.             setmetatable( tEnv, { __index = env } )
  1141.             local fnAPI, err = env.loadfile( _sPath )
  1142.             if fnAPI then
  1143.                     setfenv( fnAPI, tEnv )
  1144.                     fnAPI()
  1145.             else
  1146.                     printError( err )
  1147.                     tAPIsLoading[sName] = nil
  1148.                     return false
  1149.             end
  1150.  
  1151.             local tAPI = {}
  1152.             for k,v in pairs( tEnv ) do
  1153.                     tAPI[k] =  v
  1154.             end
  1155.  
  1156.             env[sName] = tAPI
  1157.             tAPIsLoading[sName] = nil
  1158.             return true
  1159.     end
  1160.  
  1161.     env.shell = shell
  1162.  
  1163.     setfenv( fnFile, env )
  1164.     fnFile(unpack(tArgs))
  1165. end
  1166.  
  1167. local function extract()
  1168.         local function node(path, tree)
  1169.                 if type(tree) == 'table' then
  1170.                         fs.makeDir(path)
  1171.                         for k, v in pairs(tree) do
  1172.                                 node(path .. '/' .. k, v)
  1173.                         end
  1174.                 else
  1175.                         local f = fs.open(path, 'w')
  1176.                         if f then
  1177.                                 f.write(tree)
  1178.                                 f.close()
  1179.                         end
  1180.                 end
  1181.         end
  1182.         node('', files)
  1183. end
  1184.  
  1185. local tArgs = {...}
  1186. if #tArgs == 1 and tArgs[1] == '--extract' then
  1187.     extract()
  1188. else
  1189.     run(tArgs)
  1190. end
  1191. ]]
  1192. function y.Files:Compilr(z,_,E)local T=self.path;E=E or{}local A=self:Files()if
  1193. not A[self.startup]then
  1194. error('You must have a file called '..self.startup..' to be executed at runtime.')end;local O={}
  1195. for S,H in pairs(A)do
  1196. local R=fs.open(fs.combine(T,S),"r")local D=R.readAll()R.close()if E.minify and loadstring(D)then
  1197. D=w.MinifyString(D)end;local L=O
  1198. local U={S:match((S:gsub("[^/]+/?","([^/]+)/?")))}U[#U]=nil
  1199. for H,C in pairs(U)do local M=L[C]if not M then M={}L[C]=M end;L=M end;L[fs.getName(S)]=D end
  1200. local I=j.."local files = "..a.serialize(O)..
  1201. "\n"..string.format(x,self.startup,self.startup)if E.minify then I=w.MinifyString(I)end
  1202. local N=fs.open(fs.combine(z.CurrentDirectory,_),"w")N.write(I)N.close()end
  1203. function r.Runner:Compilr(z,_,E,T)return
  1204. self:AddTask(z,T,function(A,O)_:Compilr(O,E)end):Description("Combines multiple files using Compilr"):Produces(E)end end
  1205. do
  1206. local j=[=[
  1207. local loading = {}
  1208. local oldRequire, preload, loaded = require, {}, { startup = loading }
  1209.  
  1210. local function require(name)
  1211.     local result = loaded[name]
  1212.  
  1213.     if result ~= nil then
  1214.         if result == loading then
  1215.             error("loop or previous error loading module '" .. name .. "'", 2)
  1216.         end
  1217.  
  1218.         return result
  1219.     end
  1220.  
  1221.     loaded[name] = loading
  1222.     local contents = preload[name]
  1223.     if contents then
  1224.         result = contents()
  1225.     elseif oldRequire then
  1226.         result = oldRequire(name)
  1227.     else
  1228.         error("cannot load '" .. name .. "'", 2)
  1229.     end
  1230.  
  1231.     if result == nil then result = true end
  1232.     loaded[name] = result
  1233.     return result
  1234. end
  1235. ]=]
  1236. local x="local env = setmetatable({ require = require }, { __index = getfenv() })\n"
  1237. local function z(_)return
  1238. _:gsub("%.lua$",""):gsub("/","."):gsub("^(.*)%.init$","%1")end
  1239. function y.Files:AsRequire(_,E,T)local A=self.path;T=T or{}local O=T.link;local I=self:Files()if not
  1240. I[self.startup]then
  1241. error('You must have a file called '..self.startup..' to be executed at runtime.')end;local N={j}if O then
  1242. N[#N+1]=x end
  1243. for H,R in pairs(I)do o.Verbose("Including "..H)
  1244. local D=fs.combine(A,H)N[#N+1]="preload[\""..z(H).."\"] = "
  1245. if O then assert(fs.exists(D),
  1246. "Cannot find "..H)N[#N+1]="setfenv(assert(loadfile(\""..D..
  1247. "\")), env)\n"else
  1248. local L=fs.open(D,"r")local U=L.readAll()L.close()
  1249. N[#N+1]="function(...)\n"..U.."\nend\n"end end
  1250. N[#N+1]="return preload[\""..z(self.startup).."\"](...)"
  1251. local S=fs.open(fs.combine(_.CurrentDirectory,E),"w")S.write(table.concat(N))S.close()end
  1252. function r.Runner:AsRequire(_,E,T,A)
  1253. return
  1254. self:InjectTask(h.Factory(_,A,function(O,I)E:AsRequire(I,T,O)end,h.OptionTask)):Description("Packages files together to allow require"):Produces(T)end end;local p=n.Options({...})
  1255. p:Option"verbose":Alias"v":Description"Print verbose output"
  1256. p:Option"time":Alias"t":Description"Display the time taken for tasks"
  1257. p:Option"trace":Description"Print a stack trace on errors"
  1258. p:Option"help":Alias"?":Alias"h":Description"Print this help"local v=p:Arguments()local b,g=d.FindHowl()
  1259. if not b then
  1260. if p:Get("help")or(#v==1 and
  1261. v[1]=="help")then
  1262. o.PrintColor(colours.yellow,"Howl")
  1263. o.PrintColor(colours.lightGrey,"Howl is a simple build system for Lua")
  1264. o.PrintColor(colours.grey,"You can read the full documentation online: https://github.com/SquidDev-CC/Howl/wiki/")
  1265. o.PrintColor(colours.white,(([[
  1266.             The key thing you are missing is a HowlFile. This can be "Howlfile" or "Howlfile.lua".
  1267.             Then you need to define some tasks. Maybe something like this:
  1268.         ]]):gsub("\t",""):gsub("\n+$","")))
  1269. o.PrintColor(colours.magenta,'Tasks:Minify("minify", "Result.lua", "Result.min.lua")')
  1270. o.PrintColor(colours.white,"Now just run `Howl minify`!")end;error(g,0)end
  1271. o.Verbose("Found HowlFile at "..fs.combine(g,b))
  1272. i.Subscribe({"ArgParse","changed"},function(p)
  1273. o.IsVerbose(p:Get("verbose")or false)if p:Get"help"then v={"help"}end end)local k,q=d.SetupTasks(g,b,p)
  1274. k:Task"list"(function()k:ListTasks()end):Description"Lists all the tasks"
  1275. k:Task"help"(function()o.Print("Howl [options] [task]")
  1276. o.PrintColor(colors.orange,"Tasks:")k:ListTasks("  ")
  1277. o.PrintColor(colors.orange,"\nOptions:")p:Help("  ")end):Description"Print out a detailed usage for Howl"
  1278. k:Default(function()o.PrintError("No default task exists.")
  1279. o.Verbose("Use 'Tasks:Default' to define a default task")o.PrintColor(colors.orange,"Choose from: ")
  1280. k:ListTasks("  ")end)q.dofile(fs.combine(g,b))k:RunMany(v)
Advertisement
Add Comment
Please, Sign In to add comment