local a=require'pl.utils'local b=rawget(_G,'_DEBUG')local c,d,e,f=a.patterns,a.function_arg,a.split,a.array_tostring;local g,h=table.insert,table.concat;local i=string.gsub;local io=io;local _G,print,type,tonumber,ipairs,setmetatable=_G,print,type,tonumber,ipairs,setmetatable;local j={}local k;local function l(m)return m:gsub('%s+$','')end;local function n(m)return l(m):gsub('^%s*','')end;local function o(p)return setmetatable(p,require('pl.List'))end;local function q(r,s)local t={}for u=1,#s do t[u]=r(s[u])end;return t end;local function v(w,x,y,z)local A;if y and w:match'"'then w=w:gsub('"([^"]+)"',function(B)local m,C=B:gsub(',','\001')if C>0 then A=true end;return m end)if A then A=function(m)return m:gsub('\001',',')end end end;local t=e(w,x,false,z)if y then if A then t=q(A,t)end;if w:match',$'then g(t,'')end end;return o(t)end;local function D(s,E)for u=1,#s do if E==s[u]then return u end end end;local F={column_by_name=function(self,G)if type(G)=='number'then G='$'..G end;local H={}for t in j.query(self,G)do g(H,t)end;return o(H)end,copy_select=function(self,I)I=k(I,self)local J=j.query(self,I)local t={}local K=o{J()}while#K>0 do g(t,K)K=o{J()}end;t.delim=self.delim;return j.new(t,v(I.fields,','))end,column_names=function(self)return self.fieldnames end}local L;F.__index=function(self,G)local M=F[G]if M then return M end;if not L then L=require'pl.array2d'end;return L[G]end;local N={',','\t',' ',';'}local function O(w)if w==''then return' 'end;for P,x in ipairs(N)do if w:find(x)then return x==' 'and'%s+'or x end end;return' 'end;local function Q(M,R)local S,T;local U=R=='r'if type(M)=='string'then if M=='stdin'then M=io.stdin elseif M=='stdout'then M=io.stdout else M,T=io.open(M,R)if not M then return nil,T end;S=true end end;if M and(U and not M.read or not U and not M.write)then return nil,"not a file-like object"end;return M,nil,S end;function j.read(V,W)local X,w;local Y={}if not W then W={}end;local M,T,S=Q(V,'r')if not M then return nil,T end;local Z=W.thousands_dot;local y=W.csv;if y then W.delim=','end;local tonumber=tonumber;local function _(a0)if Z then a0=a0:gsub('%.(...)','%1')end;if y and a0==''then a0='0'end;local E=tonumber(a0)if E==nil then return nil,"not a number"end;return E end;X=1;w=M:read()if not w then return nil,"empty file"end;Y.delim=W.delim and W.delim or O(w)local x=Y.delim;local a1;local a2={}local function a3(a4,a5)a1=a1 or{}g(a2,a4)g(a1,a5)end;if W.numfields then for P,z in ipairs(W.numfields)do a3(z,_)end end;local a6;if x=='%s+'and w:find(x)==1 then a6=function(m)return m:gsub('^%s+','')end;w=a6(w)end;if not W.fieldnames then local a7,a8;a7=v(w,x,y)if not W.convert then a8=q(tonumber,a7)if#a8==#a7 then g(Y,a8)for u=1,#a8 do a3(u,_)end else a8=nil end else for a4,a5 in pairs(W.convert)do a3(a4,a5)end end;if a8==nil then W.fieldnames=a7 end;w=M:read()X=X+1;if a6 then w=a6(w)end elseif type(W.fieldnames)=='string'then W.fieldnames=v(W.fieldnames,x,y)end;local a9;if W.fieldnames then Y.fieldnames=W.fieldnames;if W.last_field_collect then a9=#Y.fieldnames end;if not W.no_convert then local a7=v(w,Y.delim,y,a9)for u=1,#a7 do if not D(a2,u)and _(a7[u])then a3(u,_)end end end end;while w do if not w:find('^%s*$')then if a6 then w=a6(w)end;local a7=v(w,x,y,a9)if a1 then for aa=1,#a2 do local u,a5=a2[aa],a1[aa]local ab,T=a5(a7[u])if ab==nil then return nil,T..": "..a7[u].." at line "..X else a7[u]=ab end end end;g(Y,a7)end;w=M:read()X=X+1 end;if S then M:close()end;if x=='%s+'then Y.delim=' 'end;if not Y.fieldnames then Y.fieldnames={}end;return j.new(Y)end;local function ac(j,M,K,x)j.temp=f(K,j.temp)M:write(h(j.temp,x),'\n')end;function F:write_row(M,K)ac(self,M,K,self.delim)end;function j.write(j,V,ad,x)local M,T,S=Q(V,'w')if not M then return nil,T end;if not ad then ad=j.fieldnames end;x=x or'\t'if ad and#ad>0 then M:write(h(ad,x),'\n')end;for u=1,#j do ac(j,M,j[u],x)end;if S then M:close()end;return true end;function F:write(V)j.write(self,V,self.fieldnames,self.delim)end;local function ae(a7,af)for u=1,#a7 do local M=n(a7[u])af[u]=M;a7[u]=M:gsub('%W','_')end end;function j.new(ag,ad)ag.fieldnames=ag.fieldnames or ad or''if not ag.delim and type(ag.fieldnames)=='string'then ag.delim=O(ag.fieldnames)ag.fieldnames=v(ag.fieldnames,ag.delim)end;ag.fieldnames=o(ag.fieldnames)ag.original_fieldnames={}ae(ag.fieldnames,ag.original_fieldnames)setmetatable(ag,F)return ag end;local ah=[[
return function (t)
local i = 0
local v
local ls = {}
for i,v in ipairs(t) do
if CONDITION then
ls[#ls+1] = v
end
end
table.sort(ls,function(v1,v2)
return SORT_EXPR
end)
local n = #ls
return function()
i = i + 1
v = ls[i]
if i > n then return end
return FIELDLIST
end
end
]]local ai=[[
return function (t)
local n = #t
local i = 0
local v
return function()
repeat
i = i + 1
v = t[i]
until i > n or CONDITION
if i > n then return end
return FIELDLIST
end
end
]]local function aj(m)return type(m)=='string'end;local ak;local function al(j)return h(j.fieldnames,',')end;local function am(j,M)local a4;if M:find'^%d+$'then a4=tonumber(M)else a4=D(j.fieldnames,M)end;if a4 then return'v['..a4 ..']'else ak=M..' not found in '..al(j)return M end end;local function an(j,ao)ak=nil;local a7=ao.fields;local a2=a7:find'%$'or#j.fieldnames==0;if a7:find'^%s*%*%s*'then if not a2 then a7=al(j)else local ap=#j[1]a7={}for u=1,ap do g(a7,'$'..u)end;a7=h(a7,',')end end;local aq=c.IDEN;if a2 then aq='%$(%d+)'else a7=l(a7):gsub('[^,%w]','_')end;local am=a.bind1(am,j)local ar=i(a7,aq,am)if ak then return nil,ak end;ao.fields=a7;ao.proc_fields=ar;ao.where=ao.where or'true'if aj(ao.where)then ao.where=i(ao.where,aq,am)ak=nil end;return true end;k=function(m,j)local as;local ao={}local at,au=m:find('where ')local av,aw=m:find('sort by ')if at then as=(av or 0)-1;ao.where=m:sub(au+1,as)end;if av then ao.sort_by=m:sub(aw+1)end;as=(at or av or 0)-1;ao.fields=m:sub(1,as)local ax,T=an(j,ao)if not ax then return nil,T else return ao end end;function j.query(j,I,ay,az)local T;if aj(I)then I,T=k(I,j)if not I then return nil,T end elseif type(I)=='table'then if type(I.fields)=='table'then I.fields=h(I.fields,',')end;if not I.proc_fields then local ax,T=an(j,I)if not ax then return nil,T end end else return nil,"condition must be a string or a table"end;local aA;if I.sort_by then aA=ah else aA=ai end;local a7=I.proc_fields or I.fields;if az then a7='{'..a7 ..'}'end;aA=aA:gsub('FIELDLIST',a7)if aj(I.where)then aA=aA:gsub('CONDITION',I.where)I.where=nil else aA=aA:gsub('CONDITION','_condn(v)')I.where=d(0,I.where,'condition.where must be callable')end;if I.sort_by then local aB,aC,aD;local aE=I.sort_by;local aF,aG=aE:find('%s+')if aF then aC,aD=aE:sub(1,aF-1),aE:sub(aG+1)else aC=aE;aD='asc'end;if aC:match'^%$'then aC=aC:sub(2)end;aC=am(j,aC)if ak then return nil,ak end;if aD=='asc'then aD='<'else aD='>'end;aB=('%s %s %s'):format(aC:gsub('v','v1'),aD,aC:gsub('v','v2'))aA=aA:gsub('SORT_EXPR',aB)end;if I.where then aA='return function(_condn) '..aA..' end'end;if b then print(aA)end;local aH,T=a.load(aA,'tmp')if not aH then return nil,T end;aH=aH()if I.where then aH=aH(I.where)end;local aI=aH(j)if ay then g(ay,_G)local aJ={}a.setfenv(aI,aJ)setmetatable(aJ,{__index=function(aK,aL)for aa,s in ipairs(ay)do if s[aL]then return s[aL]end end end})end;return aI end;F.select=j.query;F.select_row=function(ag,I,ay)return j.query(ag,I,ay,true)end;function j.filter(aM,aN,aO,aP)local ag=j.read(aN or'stdin')local aQ=Q(aO or'stdout')local J,T=ag:select(aM)local x=ag.delim;if not J then T='error: '..T;if aP then return nil,T else a.quit(1,T)end end;while true do local t={J()}if#t==0 then break end;aQ:write(h(t,x),'\n')end end;return j