--[[ Literate Pandoc & Fennel Bundle: A Pandoc filter for literate and natural programming Fennel: (C) 2016-2023 Calvin Rose and contributors License: MIT License https://git.sr.ht/~technomancy/fennel/tree/main/item/LICENSE Source: https://sr.ht/~technomancy/fennel or https://github.com/bakpakin/Fennel/issues Website: https://fennel-lang.org Literate Pandoc: (C) 2023 perro hi@perrotuerto.blog License: GPLv3 https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/LICENSE.txt Source: https://git.cuates.net/perro/literate-pandoc The following code is minified, check Literate Pandoc source for a readable version ]]-- package.preload["fennel.repl"]=package.preload["fennel.repl"]or function(...) local o=require("fennel.utils")local UesS=require("fennel.parser") local CZ=require("fennel.compiler")local iD90MK=require("fennel.specials") local ZuYO=require("fennel.view")local j=(table.unpack or _G.unpack) local function t(aBv0K6z) local function GI()if(0 < aBv0K6z["stack-size"])then return".."else return">> "end end;io.write(GI())io.flush()local nsm9=io.read()return (nsm9 and(nsm9 .."\n"))end;local function NChC(kO)io.write(table.concat(kO,"\9")) return io.write("\n")end local function Z4(FIHZeLmK,Gho7Jfcz,pi3tYW) local function nt()local R7iAD=FIHZeLmK if (R7iAD=="Lua Compile")then return ("Bad code generated - likely a bug with the compiler:\n".."--- Generated Lua Start ---\n".. pi3tYW.."--- Generated Lua End ---\n")elseif(R7iAD== "Runtime")then return (CZ.traceback(tostring(Gho7Jfcz),4).."\n")elseif true then local Re6P_=R7iAD;return ("%s error: %s\n"):format(FIHZeLmK,tostring(Gho7Jfcz))else return nil end end;return io.write(nt())end local function I13Z9(VNtfqHq,wB,cmQxB1bj)local aozwWK do local G0AXlB={}local Pv=#G0AXlB for HW in pairs(VNtfqHq.___replLocals___)do local cWzY=("local %s = ___replLocals___['%s']"):format(HW,HW) if(nil~=cWzY)then Pv=(Pv+1)do end(G0AXlB)[Pv]=cWzY else end end;aozwWK=G0AXlB end;local GbnuD do local ESO={}local DEb=#ESO for Uzk3,LVrOH0 in pairs(cmQxB1bj.manglings)do local YLRWb;if not cmQxB1bj.gensyms[LVrOH0]then YLRWb=("___replLocals___['%s'] = %s"):format(LVrOH0,LVrOH0)else YLRWb=nil end;if (nil~=YLRWb)then DEb=(DEb+1)do end(ESO)[DEb]=YLRWb else end end;GbnuD=ESO end;local CW2;if wB:find("\n")then CW2="\n"else CW2=" "end;local function vlSSXJ() if next(aozwWK)then return( table.concat(aozwWK," ")..CW2)else return""end end local function k() local u6sl,F=wB:match("^(.*)[\n ](return .*)$") if((nil~=u6sl)and(nil~=F))then local fgPC0lUc=u6sl;local oRfi=F;return (fgPC0lUc..CW2 .. table.concat(GbnuD," ")..CW2 ..oRfi)elseif true then local CrAGWh=u6sl;return wB else return nil end end;return(vlSSXJ()..k())end local function tj(d,sCjf4,y)local P=2000;local Av={}local nw={}local OVuV52eb=y:gsub(".*[%s)(]+","")local c=false local function Td(yqTH,gcTeY,Ce) local QzyH5hCR=( (gcTeY==d)or(gcTeY==d.___replLocals___))local hYkd7=nw;local UKZpzj=#hYkd7;local function Fniffr() if QzyH5hCR then return sCjf4.manglings else return gcTeY end end for FMt_pX,ouz in o.allpairs(Fniffr())do if (P<=#nw)then break end;local K1TX5 do local BYnM1VtZ if QzyH5hCR then BYnM1VtZ=ouz else BYnM1VtZ=FMt_pX end if ( (type(FMt_pX)=="string")and (yqTH==FMt_pX:sub(0,#yqTH))and not Av[FMt_pX]and((":"~=Ce:sub(-1))or ("function"==type(gcTeY[BYnM1VtZ]))))then Av[FMt_pX]=true;K1TX5=(Ce..FMt_pX)else K1TX5=nil end end if(nil~=K1TX5)then UKZpzj=(UKZpzj+1)do end(hYkd7)[UKZpzj]=K1TX5 else end end;return hYkd7 end local function KlzsEvn(djzP,b_,Q1w1zKw,a5F,Ue0N)local L99;if Ue0N then L99="^([^:]+):(.*)"else L99="^([^.]+)%.(.*)"end local B3,PL7_Dl=djzP:match(L99)local N8V=(sCjf4.manglings[B3]or B3) if( type(b_[N8V])=="table")then c=true;if Ue0N then return Td(PL7_Dl,b_[N8V],(Q1w1zKw..B3 ..":"))else return a5F(PL7_Dl,b_[N8V],(Q1w1zKw..B3))end else return nil end end local function eOfvjl1(b,kCfK5,TJ5Kz5NO)local Pke1T8 if TJ5Kz5NO then Pke1T8=(TJ5Kz5NO..".")else Pke1T8=""end if(not b:find("%.")and b:find(":"))then return KlzsEvn(b,kCfK5,Pke1T8,eOfvjl1,true)elseif not b:find("%.")then return Td(b,kCfK5,Pke1T8)else return KlzsEvn(b,kCfK5,Pke1T8,eOfvjl1,false)end end for HKyO,mx in ipairs({sCjf4.specials,sCjf4.macros,(d.___replLocals___ or{}),d,d._G})do if c then break end;eOfvjl1(OVuV52eb,mx)end;return nw end;local EgG={}local function YgsJ(od)return od:match("^%s*,")end local function aV1Rus()local iRgs do local KYSkMOxM9={}local m1=#KYSkMOxM9 for L,uApola2 in pairs(EgG)do local jbLeA=(" ,%s - %s"):format(L,( (CZ.metadata):get(uApola2,"fnl/docstring")or"undocumented")) if(nil~=jbLeA)then m1=(m1+1)do end(KYSkMOxM9)[m1]=jbLeA else end end;iRgs=KYSkMOxM9 end;return table.concat(iRgs,"\n")end EgG.help=function(Jy,KDEqSly,w2hBXVO) return w2hBXVO({("Welcome to Fennel.\nThis is the REPL where you can enter code to be evaluated.\nYou can also run these repl commands:\n\n".. aV1Rus().. "\n ,exit - Leave the repl.\n\nUse ,doc something to see descriptions for individual macros and special forms.\n\nFor more information about the language, see https://fennel-lang.org/reference")})end;do end (CZ.metadata):set(EgG.help,"fnl/docstring","Show this message.") local function Gsx955(Q,c9MGuH,aOuScj,XXtE6Ji) local J,oyvqbnx=pcall(iD90MK["load-code"]("return require(...)",c9MGuH),Q) if((J==true)and(nil~=oyvqbnx))then local YdCSDvl=oyvqbnx;local Cum;package.loaded[Q]= nil;Cum=nil;local Je_,x=pcall(require,Q)local _nbhL;if not Je_ then aOuScj({x}) _nbhL=YdCSDvl else _nbhL=x end iD90MK["macro-loaded"][Q]=nil if ((type(YdCSDvl)=="table")and(type(_nbhL)=="table"))then for VJ_RQcYw,bgDxqiW in pairs(_nbhL)do YdCSDvl[VJ_RQcYw]=bgDxqiW end;for u in pairs(YdCSDvl)do if(nil== (_nbhL)[u])then YdCSDvl[u]=nil else end end;package.loaded[Q]=YdCSDvl else end;return aOuScj({"ok"})elseif ((J==false)and(nil~=oyvqbnx))then local n_=oyvqbnx if n_:match("loop or previous error loading module")then package.loaded[Q]= nil;return Gsx955(Q,c9MGuH,aOuScj,XXtE6Ji)elseif (iD90MK["macro-loaded"])[Q]then iD90MK["macro-loaded"][Q]=nil;return nil else local function a() local xei=n_:gsub("\n.*","")return xei end;return XXtE6Ji("Runtime",a())end else return nil end end local function _bh_k(zHHW,H,OQYu)local k9,bRREug,R_RJfoeP=pcall(zHHW) if((k9 ==true)and(bRREug==true)and(nil~= R_RJfoeP))then local HuvPsV=R_RJfoeP;return OQYu(HuvPsV)elseif(k9 ==false)then return H("Parse","Couldn't parse input.")else return nil end end EgG.reload=function(pZ8,J12U,jzU,nrU) local function Bi7(EW4bXR)return Gsx955(tostring(EW4bXR),pZ8,jzU,nrU)end;return _bh_k(J12U,nrU,Bi7)end;do end (CZ.metadata):set(EgG.reload,"fnl/docstring","Reload the specified module.") EgG.reset=function(vL_Vj_v,fi3,hbfOHU)vL_Vj_v.___replLocals___={}return hbfOHU({"ok"})end;do end (CZ.metadata):set(EgG.reset,"fnl/docstring","Erase all repl-local scope.") EgG.complete=function(GpItItxe,O,Equz,dsN,HzKU,Zbl8) local function I_iWZE2I()return Equz(tj(GpItItxe,HzKU,string.char(j(Zbl8)):gsub(",complete +",""):sub(1, -2)))end;return _bh_k(O,dsN,I_iWZE2I)end;do end (CZ.metadata):set(EgG.complete,"fnl/docstring","Print all possible completions for a given input symbol.") local function w7(c5q,iXFMywMG,a,RgyIZgQm,Kz) for hncJ5,oMSPa in pairs(iXFMywMG)do if (("string"==type(hncJ5))and(package~=oMSPa))then local _pJ7=type(oMSPa) if(_pJ7 =="function")then if ((a..hncJ5)):match(c5q)then table.insert(Kz,(a..hncJ5))else end elseif(_pJ7 =="table")then if not RgyIZgQm[oMSPa]then local sa6R;do local gH=RgyIZgQm gH[oMSPa]=true;sa6R=gH end w7(c5q,oMSPa,(a..hncJ5:gsub("%.","/").."."),sa6R,Kz)else end else end else end end;return Kz end local function drEf10P(mRsiV)local PbSET=w7(mRsiV,package.loaded,"",{},{})local hpVB={} local YWvn1scCO=#hpVB for _u9Y,_BGX in ipairs(PbSET)do local m4bfrLhD=_BGX:gsub("^_G%.","") if(nil~=m4bfrLhD)then YWvn1scCO=( YWvn1scCO+1)do end(hpVB)[YWvn1scCO]=m4bfrLhD else end end;return hpVB end EgG.apropos=function(phlgUmyq,y1uQkA,n_b,X5Kb,IjSJ6) local function nDieWGG(P)return n_b(drEf10P(tostring(P)))end;return _bh_k(y1uQkA,X5Kb,nDieWGG)end;do end (CZ.metadata):set(EgG.apropos,"fnl/docstring","Print all functions matching a pattern in all loaded modules.") local function U9bX9Qj(_li)local VgksUV do local egVOS={}local L7hI7N16=#egVOS;for d in _li:gmatch("[^%.]+")do local H0J=d if(nil~=H0J)then L7hI7N16=( L7hI7N16+1)do end(egVOS)[L7hI7N16]=H0J else end end;VgksUV=egVOS end;local lD8E0=package.loaded for oLsc,qXd in ipairs(VgksUV)do if(nil==lD8E0)then break end;local cZ8mW;do local U=qXd:gsub("%/",".")cZ8mW=U end;lD8E0=lD8E0[cZ8mW]end;return lD8E0 end local function F3vF(MA)local RmPOf3Wl={}local ROso3G=#RmPOf3Wl for AaZVi3,A in ipairs(drEf10P(".*"))do local X7eznY do local nui3w2g=U9bX9Qj(A) if("function"==type(nui3w2g))then local TyY1TP=(CZ.metadata):get(nui3w2g,"fnl/docstring")if(nil~=TyY1TP)then local I=TyY1TP;X7eznY=(I:match(MA)and A)else X7eznY=nil end else X7eznY=nil end end;if(nil~=X7eznY)then ROso3G=(ROso3G+1)do end (RmPOf3Wl)[ROso3G]=X7eznY else end end;return RmPOf3Wl end EgG["apropos-doc"]=function(p,nWwv,U5,SSRIM2dF,TQ)local function PZd(lp2390NG) return U5(F3vF(tostring(lp2390NG)))end;return _bh_k(nWwv,SSRIM2dF,PZd)end;do end (CZ.metadata):set(EgG["apropos-doc"],"fnl/docstring","Print all functions that match the pattern in their docs") local function l1YXXES(Vc,NV) for f_qSS3,jkNMk in ipairs(drEf10P(NV))do local C4yJBj5=U9bX9Qj(jkNMk) if ( ("function"==type(C4yJBj5))and(CZ.metadata):get(C4yJBj5,"fnl/docstring"))then Vc(iD90MK.doc(C4yJBj5,jkNMk))Vc()else end end;return nil end EgG["apropos-show-docs"]=function(Kdb,A8f,f,bvP)local function fRpw3tSl(Wj3o4LNJ) return l1YXXES(f,tostring(Wj3o4LNJ))end;return _bh_k(A8f,bvP,fRpw3tSl)end;do end (CZ.metadata):set(EgG["apropos-show-docs"],"fnl/docstring","Print all documentations matching a pattern in function name") local function QO9Kq(aq67Ya3,x,LJVh6)local pqvpad=x;local pA=pqvpad["___replLocals___"]local BU1O2_L=pqvpad;local uZM;local function ZcEsZ(pwz7,Kt_w)return(pA[Kt_w]or BU1O2_L[Kt_w])end uZM=setmetatable({},{__index=ZcEsZ}) local Q56iTd,no8ruIR=pcall(CZ["compile-string"],tostring(aq67Ya3),{scope=LJVh6}) if((Q56iTd==true)and(nil~=no8ruIR))then local F=no8ruIR;return iD90MK["load-code"](F,uZM)()else return nil end end EgG.find=function(S0,Sm,hViv5C_,jh,Pwq) local function q16(k1oD)local P7WPKR do local O=o["sym?"](k1oD)if(nil~=O)then local nZ0YB=QO9Kq(O,S0,Pwq) if(nil~=nZ0YB)then P7WPKR=debug.getinfo(nZ0YB)else P7WPKR=nZ0YB end else P7WPKR=O end end if ( (_G.type(P7WPKR)=="table")and ((P7WPKR).what=="Lua")and(nil~= (P7WPKR).source)and(nil~= (P7WPKR).linedefined)and(nil~= (P7WPKR).short_src))then local p6e=(P7WPKR).source;local po8N=(P7WPKR).linedefined local a7=(P7WPKR).short_src;local U6w do local Irt=CZ.sourcemap;if(nil~=Irt)then Irt=(Irt)[p6e]else end;if(nil~= Irt)then Irt=(Irt)[po8N]else end;if(nil~=Irt)then Irt=(Irt)[2]else end;U6w=Irt end;return hViv5C_({string.format("%s:%s",a7,(U6w or po8N))})elseif(P7WPKR==nil)then return jh("Repl","Unknown value")elseif true then local khsuDn=P7WPKR;return jh("Repl","No source info")else return nil end end;return _bh_k(Sm,jh,q16)end;do end (CZ.metadata):set(EgG.find,"fnl/docstring","Print the filename and line number for a given function") EgG.doc=function(iDQAM,dJEEzHLA,VhIhDh,rSwA,IuZ) local function uQBNaZ8L(amTY)local YbIZJ=tostring(amTY) local kVCVg=(o["multi-sym?"](YbIZJ)or{YbIZJ})local E5J5PpiD,yxld=nil,nil local function rn0()return (o["get-in"](IuZ.specials,kVCVg)or o["get-in"](IuZ.macros,kVCVg)or QO9Kq(YbIZJ,iDQAM,IuZ))end;E5J5PpiD,yxld=pcall(rn0) if E5J5PpiD then return VhIhDh({iD90MK.doc(yxld,YbIZJ)})else return rSwA("Repl","Could not resolve value for docstring lookup")end end;return _bh_k(dJEEzHLA,rSwA,uQBNaZ8L)end;do end (CZ.metadata):set(EgG.doc,"fnl/docstring","Print the docstring and arglist for a function, macro, or special form.") EgG.compile=function(Kz3Upt,FNcg,ip1Y,SNe51hiO,nbh) local function xyD5eM3(dvQC_Q) local w7pQl=iD90MK["current-global-names"](Kz3Upt) local F4cjGy,D5=pcall(CZ.compile,dvQC_Q,{env=Kz3Upt,scope=nbh,allowedGlobals=w7pQl}) if F4cjGy then return ip1Y({D5})else return SNe51hiO("Repl",("Error compiling expression: "..D5))end end;return _bh_k(FNcg,SNe51hiO,xyD5eM3)end;do end (CZ.metadata):set(EgG.compile,"fnl/docstring","compiles the expression into lua and prints the result.") local function eG8QRxg(K46A) for B,DIW in ipairs((K46A or{}))do for mcrg,zg74G in pairs(DIW)do local C4Qpi_bm=mcrg:match("^repl%-command%-(.*)")if(nil~=C4Qpi_bm)then local nsBJr=C4Qpi_bm EgG[nsBJr]=(EgG[nsBJr]or zg74G)else end end end;return nil end local function YjH(y0,KkoR7,rs,l8eg,YAV,F_XBNQq,QTmbwc,Qa95Sy)local M8OIL=y0:match(",([^%s/]+)") do local aeH=EgG[M8OIL] if(nil~=aeH)then local LPJB=aeH;LPJB(l8eg,KkoR7,YAV,F_XBNQq,QTmbwc,Qa95Sy)elseif true then local zwqy0k=aeH if("exit"~= M8OIL)then YAV({"Unknown command",M8OIL})else end else end end;if("exit"~=M8OIL)then return rs()else return nil end end local function C(_5SaCc,ywU,vd2O) if ywU then if vd2O.set_readline_name then vd2O.set_readline_name("fennel")else end;vd2O.set_options({keeplines=1000,histfile=""}) _5SaCc.readChunk=function(L) local hlKRv if(0 ")):gsub("\n$",""):gsub("\n","\n ")local BhRI=getmetatable(_qwQtun2) if ( (type(_qwQtun2)=="function")or((type(BhRI)=="table")and (type(BhRI.__call)=="function")))then local Hnykp=table.concat(((AHA.metadata):get(_qwQtun2,"fnl/arglist")or {"#"})," ")local KVZGVS;if(0 <#Hnykp)then KVZGVS=" "else KVZGVS=""end;return string.format("(%s%s%s)\n %s",HJ,KVZGVS,Hnykp,lEZKOn)else return string.format("%s\n %s",HJ,lEZKOn)end end end local function qh0m(W,l,YtJ3Vld,gesxk) AHA.metadata[O11E[W]]={["fnl/arglist"]=l,["fnl/docstring"]=YtJ3Vld,["fnl/body-form?"]=gesxk}return nil end local function T1pL(RPBF,MA,cQiY,mi0Su5)local RSbaegr=(mi0Su5 or 2)local D_XnV=#RPBF local T5b=AHA["make-scope"](MA) for Pzvq9=RSbaegr,D_XnV do AHA.compile1(RPBF[Pzvq9],T5b,cQiY,{nval=0})end;return nil end O11E["do"]=function(xWDqizI,lLfybj,kfv75hSt,L,QtUzJA8W,vyg,i,iu)local AQAZ71dv=(QtUzJA8W or 2) local hnm7wW0=(i or AHA["make-scope"](lLfybj))local UM52=(vyg or{})local D_=#xWDqizI;local rVtCZOT={returned=true} local function zXaijdUvQ(RZ7Pv3B4,utllnvh,Cy) if (D_>")fB2Vks("band","0","0","&")fB2Vks("bor","0","0","|") fB2Vks("bxor","0","0","~") qh0m("lshift",{"x","n"},"Bitwise logical left shift of x by n bits.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.") qh0m("rshift",{"x","n"},"Bitwise logical right shift of x by n bits.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.") qh0m("band",{"x1","x2","..."},"Bitwise AND of any number of arguments.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.") qh0m("bor",{"x1","x2","..."},"Bitwise OR of any number of arguments.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.") qh0m("bxor",{"x1","x2","..."},"Bitwise XOR of any number of arguments.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.") qh0m("..",{"a","b","..."},"String concatenation operator; works the same as Lua but accepts more arguments.") local function KEar3_f(D7CGKmb8,uLyz97,BWkW1j,yZA)local WXYCK=uLyz97;local ifeZY=WXYCK[1]local hAzmuHjD=WXYCK[2]local wOpIPj=WXYCK[3] local ezsAVF9Y=AHA.compile1(hAzmuHjD,BWkW1j,yZA,{nval=1})local wMf1B=ezsAVF9Y[1] local b=AHA.compile1(wOpIPj,BWkW1j,yZA,{nval=1})local lObXzZtF=b[1]return string.format("(%s %s %s)",tostring(wMf1B),D7CGKmb8,tostring(lObXzZtF))end local function Bp(gXOgxVqi,aEAOdyb,aANaGdmD,gJ,I)local Iuy do local CGX={}local YOdo=#CGX for Ts=2,#aANaGdmD do local Xoxdbm=tostring((AHA.compile1(aANaGdmD[Ts],gJ,I,{nval=1}))[1]) if(nil~=Xoxdbm)then YOdo=(YOdo+1)do end(CGX)[YOdo]=Xoxdbm else end end;Iuy=CGX end;local xpU0HjP5 do local v={}local TpRHAsM=#v for O872Y=1,(#Iuy-1)do local Fhc=string.format("(%s %s %s)",Iuy[O872Y],gXOgxVqi,Iuy[(O872Y+1)]) if(nil~=Fhc)then TpRHAsM=(TpRHAsM+1)do end(v)[TpRHAsM]=Fhc else end end;xpU0HjP5=v end local E8GAk=string.format(" %s ",(aEAOdyb or"and"))return table.concat(xpU0HjP5,E8GAk)end local function WRTH(Q19nu,tG,v7Zovu3S,R8Mr,E7pBdDI)local f5qj={}local bY={}local hi1fT={} local d=string.format(" %s ",(tG or"and")) for Ev=2,#v7Zovu3S do table.insert(f5qj,tostring(AHA.gensym(R8Mr))) table.insert(hi1fT,tostring((AHA.compile1(v7Zovu3S[Ev],R8Mr,E7pBdDI,{nval=1}))[1]))end do local PinF3=bY;local Fl=#PinF3 for _RHbBQe_=1,(#f5qj-1)do local csZGJji=string.format("(%s %s %s)",f5qj[_RHbBQe_],Q19nu,f5qj[(_RHbBQe_+1)]) if(nil~=csZGJji)then Fl=(Fl+1)do end(PinF3)[Fl]=csZGJji else end end end;return string.format("(function(%s) return %s end)(%s)",table.concat(f5qj,","),table.concat(bY,d),table.concat(hi1fT,","))end local function s(_IQM21V,c8S3fu,Z0) do local cAn=(c8S3fu or _IQM21V) local function zdq3gf(h574,nrS48GsH,VA8mi) AHA.assert((2 <#h574),"expected at least two arguments",h574) if(3 ==#h574)then return KEar3_f(cAn,h574,nrS48GsH,VA8mi)elseif Jzh["every?"](Jzh["idempotent-expr?"],{cI0i(h574,2)})then return Bp(cAn,Z0,h574,nrS48GsH,VA8mi)else return WRTH(cAn,Z0,h574,nrS48GsH,VA8mi)end end;O11E[_IQM21V]=zdq3gf end;return qh0m(_IQM21V,{"a","b","..."},"Comparison operator; works the same as Lua but accepts more arguments.")end;s(">")s("<")s(">=")s("<=")s("=","==") s("not=","~=","or") local function gPVgI8V(pva,orki) local function RqeYK(ojg0_C,mgD1,Bc) AHA.assert((#ojg0_C==2),"expected one argument",ojg0_C)local bJwiKR=AHA.compile1(ojg0_C[2],mgD1,Bc,{nval=1}) return( (orki or pva)..tostring(bJwiKR[1]))end;O11E[pva]=RqeYK;return nil end;gPVgI8V("not","not ") qh0m("not",{"x"},"Logical operator; works the same as Lua.")gPVgI8V("bnot","~") qh0m("bnot",{"x"},"Bitwise negation; only works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")gPVgI8V("length","#") qh0m("length",{"x"},"Returns the length of a table or string.")do end(O11E)["~="]=O11E["not="]O11E["#"]=O11E.length O11E.quote=function(fCYOnUn,z5VJbT,I8OjNIh) AHA.assert(( #fCYOnUn==2),"expected one argument",fCYOnUn)local BTxuZzK,qdtphyF=true,z5VJbT while qdtphyF do qdtphyF=qdtphyF.parent;if (qdtphyF==AHA.scopes.compiler)then BTxuZzK=false else end end return AHA["do-quote"](fCYOnUn[2],z5VJbT,I8OjNIh,BTxuZzK)end qh0m("quote",{"x"},"Quasiquote the following form. Only works in macro/compiler scope.")local uA5vZEb={} local function ZNbFB(F)local qDcOp4_=getmetatable(F) assert((qDcOp4_~=getmetatable("")),"Illegal metatable access!")return qDcOp4_ end;local SE0lo2=nil local function GaxZqb()local Rafg8;do local N=rawget(_G,"utf8")if(nil~=N)then Rafg8=Jzh.copy(N)else Rafg8=N end end return {table=Jzh.copy(table),math=Jzh.copy(math),string=Jzh.copy(string),pairs=Jzh.stablepairs,ipairs=ipairs,select=select,tostring=tostring,tonumber=tonumber,bit=rawget(_G,"bit"),pcall=pcall,xpcall=xpcall,next=next,print=print,type=type,assert=assert,error=error,setmetatable=setmetatable,getmetatable=ZNbFB,require=SE0lo2,rawlen=rawget(_G,"rawlen"),rawget=rawget,rawset=rawset,rawequal=rawequal,_VERSION=_VERSION,utf8=Rafg8}end local function ZPz3(_5CGI6PZ)local Z={}local ipJKYa=getmetatable(_5CGI6PZ) local dPQffKj=ipJKYa["__index"]if("table"==type(dPQffKj))then for JSQ,JhH in pairs(dPQffKj)do Z[JSQ]=JhH end else end for uh,gWvKWBjLE in next,_5CGI6PZ,nil do Z[uh]=gWvKWBjLE end;return next,Z,nil end local function gwje(RsymxL,Iz,xzf,EgCB_nf)local P do local vQoZJVom=(EgCB_nf or Jzh.root.options) if(( _G.type(vQoZJVom)=="table")and ((vQoZJVom)["compiler-env"]=="strict"))then P=GaxZqb()elseif((_G.type(vQoZJVom)=="table")and (nil~= (vQoZJVom).compilerEnv))then local ftq9dIXI=(vQoZJVom).compilerEnv;P=ftq9dIXI elseif ((_G.type(vQoZJVom)=="table")and(nil~= (vQoZJVom)["compiler-env"]))then local FX=(vQoZJVom)["compiler-env"]P=FX elseif true then local zGwGt3E=vQoZJVom P=GaxZqb(false)else P=nil end end;local kViUhgj;local function UyTq(vm7_I)return Jzh.sym(AHA.gensym((AHA.scopes.macro or Iz),vm7_I))end;local function p()return AHA.scopes.macro end;local function u6_LlQ46(L6C5NIeG) AHA.assert(AHA.scopes.macro,"must call from macro",RsymxL) return AHA.scopes.macro.manglings[tostring(L6C5NIeG)]end local function GAPJTX(sgg_50x) AHA.assert(AHA.scopes.macro,"must call from macro",RsymxL)return AHA.macroexpand(sgg_50x,AHA.scopes.macro)end kViUhgj={_AST=RsymxL,_CHUNK=xzf,_IS_COMPILER=true,_SCOPE=Iz,_SPECIALS=AHA.scopes.global.specials,_VARARG=Jzh.varg(),["macro-loaded"]=uA5vZEb,unpack=cI0i,["assert-compile"]=AHA.assert,view=fNDs,version=Jzh.version,metadata=AHA.metadata,["ast-source"]=Jzh["ast-source"],list=Jzh.list,["list?"]=Jzh["list?"],["table?"]=Jzh["table?"],sequence=Jzh.sequence,["sequence?"]=Jzh["sequence?"],sym=Jzh.sym,["sym?"]=Jzh["sym?"],["multi-sym?"]=Jzh["multi-sym?"],comment=Jzh.comment,["comment?"]=Jzh["comment?"],["varg?"]=Jzh["varg?"],gensym=UyTq,["get-scope"]=p,["in-scope?"]=u6_LlQ46,macroexpand=GAPJTX}kViUhgj._G=kViUhgj;return setmetatable(kViUhgj,{__index=P,__newindex=P,__pairs=ZPz3})end local function lidmlMiV(...)local bWlu={}local aS=#bWlu for a3KhFIf in string.gmatch((package.config or""),"([^\n]+)")do local sAPG6y=a3KhFIf if(nil~=sAPG6y)then aS=(aS+1)do end(bWlu)[aS]=sAPG6y else end end;return bWlu end;local qr0C1=lidmlMiV(...)local g=qr0C1[1]local o=qr0C1[2]local gAN1v8=qr0C1[3] local pL4fol={dirsep=(g or"/"),pathmark=( gAN1v8 or";"),pathsep=(o or"?")} local function nmhV(m)return string.gsub(m,"[^%w]","%%%1")end local function Wd1a_i(A,fd2H)local zQ5=nmhV(pL4fol.pathsep) local xhs=("([^%s]*)%s"):format(zQ5,zQ5)local Bd=A:gsub("%.",pL4fol.dirsep) local n4Rf=((fd2H or Jzh["fennel-module"].path)..pL4fol.pathsep) local function QT(vGQaEDQ) local HpHT=vGQaEDQ:gsub(nmhV(pL4fol.pathmark),Bd)local e=vGQaEDQ:gsub(nmhV(pL4fol.pathmark),A)local OiRV=( io.open(HpHT)or io.open(e)) if(nil~=OiRV)then local kV=OiRV;kV:close()return HpHT elseif true then local Ieet0=OiRV return nil, ("no file '"..HpHT.."'")else return nil end end local function hjd(IZMQx0rC,BrM2hYPE)local igszykf=n4Rf:match(xhs,IZMQx0rC) if(nil~=igszykf)then local q_v=igszykf local I,H=QT(q_v) if(nil~=I)then local aTdQOCcy=I;return aTdQOCcy elseif((I==nil)and(nil~=H))then local zSyor=H;local function HB()local fuSiMn=( BrM2hYPE or{})table.insert(fuSiMn,zSyor) return fuSiMn end;return hjd((IZMQx0rC+#q_v+1),HB())else return nil end elseif true then local ZsrTm=igszykf;local function v7Q() local oWrDs=table.concat((BrM2hYPE or{}),"\n\9") if(_VERSION<"Lua 5.4")then return("\n\9"..oWrDs)else return oWrDs end end;return nil, v7Q()else return nil end end;return hjd(1)end local function ur(C9m4) local function dVKfuf(ulHu)local kIuvqs_z=Jzh.copy(Jzh.root.options)for WXKWwF,nGfnz9aW in pairs((C9m4 or{}))do kIuvqs_z[WXKWwF]=nGfnz9aW end kIuvqs_z["module-name"]=ulHu;local DiibeTp,VE=Wd1a_i(ulHu) if(nil~=DiibeTp)then local Jpkn2x2L=DiibeTp;local qIGM do local zd=Jpkn2x2L;local aIh=kIuvqs_z;local function Ox(...) return Jzh["fennel-module"].dofile(zd,aIh,...)end;qIGM=Ox end;return qIGM,Jpkn2x2L elseif((DiibeTp==nil)and(nil~=VE))then local T5K7=VE;return T5K7 else return nil end end;return dVKfuf end local function IAEw(VXa0K,EcGb,GFIjpl,...) local CXtt1l9J=(package.loaders or package.searchers or{})local MIJA=table.insert(CXtt1l9J,1,VXa0K) local _rbAfa=Jzh["fennel-module"].dofile(EcGb,GFIjpl,...)table.remove(CXtt1l9J,1)return _rbAfa end local function HkFs_(WC)local dpl5Qr8i do local vdrp=Jzh.copy(Jzh.root.options)do end (vdrp)["module-name"]=WC;vdrp["env"]="_COMPILER"vdrp["requireAsInclude"]=false;vdrp["allowedGlobals"]= nil;dpl5Qr8i=vdrp end local VhWZl_=Wd1a_i(WC,Jzh["fennel-module"]["macro-path"]) if(nil~=VhWZl_)then local xB=VhWZl_;local qtSF2kh if (dpl5Qr8i["compiler-env"]==_G)then local Bu=HkFs_;local ja=xB;local glp=dpl5Qr8i local function oP(...)return IAEw(Bu,ja,glp,...)end;qtSF2kh=oP else local J92nyajw=xB;local wBBO=dpl5Qr8i;local function k3gZQQ(...)return Jzh["fennel-module"].dofile(J92nyajw,wBBO,...)end qtSF2kh=k3gZQQ end;return qtSF2kh,xB else return nil end end local function a6y99PLZ(TW5ElhE)local ytAdXO=Wd1a_i(TW5ElhE,package.path) if(nil~=ytAdXO)then local QzBDtoL=ytAdXO;local Wh5WXYH do local BIqcT3KO=io.open(QzBDtoL) local function Ggug(as,...)BIqcT3KO:close()if as then return...else return error(...,0)end end local function D4()return assert(BIqcT3KO:read("*a"))end Wh5WXYH=Ggug(_G.xpcall(D4,(package.loaded.fennel or debug).traceback))end;local _j=WpQBPz(Wh5WXYH,gwje(),QzBDtoL)return _j,QzBDtoL else return nil end end;local Maun0kCg={HkFs_,a6y99PLZ} local function X1okTW(bKRB,bqOmBw)local UlRBdf2T=Maun0kCg[bqOmBw] if (nil~=UlRBdf2T)then local WqG=UlRBdf2T;local jQ6ByW,hI=WqG(bKRB) if((nil~=jQ6ByW)and true)then local X=jQ6ByW;local fvOp=hI;return X,fvOp elseif true then local xIB=jQ6ByW;return X1okTW(bKRB,(bqOmBw+1))else return nil end else return nil end end local function IL(MJ) if ((MJ=="fennel.macros")or (package and package.loaded and("table"== type(package.loaded[MJ]))and( package.loaded[MJ].metadata==AHA.metadata)))then return{metadata=AHA.metadata,view=fNDs}else return nil end end local function Txnp(gk4oh) local function drQ()local WxqsgF,B7=X1okTW(gk4oh,1) AHA.assert(WxqsgF,(gk4oh.." module not found."))do end(uA5vZEb)[gk4oh]=WxqsgF(gk4oh,B7) return uA5vZEb[gk4oh]end return(uA5vZEb[gk4oh]or IL(gk4oh)or drQ())end;SE0lo2=Txnp local function k7(DRaen,cOdno7,Q7NwnLvM) AHA.assert(Jzh["table?"](DRaen),"expected macros to be table",cOdno7) for b55dl,vv7Mbm in pairs(DRaen)do AHA.assert((type(vv7Mbm)=="function"),"expected each macro to be function",cOdno7) AHA["check-binding-valid"](Jzh.sym(b55dl),Q7NwnLvM,cOdno7,{["macro?"]=true})do end(Q7NwnLvM.macros)[b55dl]=vv7Mbm end;return nil end local function i4bMH(iHFG,mbY,klPjHrEL,fT)local iRHgEZ1=iHFG;local qe3O8z=iRHgEZ1["filename"]local _qk=iRHgEZ1[2] local WwYOV=(qe3O8z or( Jzh["table?"](_qk)and _qk.filename))local TzOhP=Jzh.root.options["module-name"] local DYbu7=AHA.compile(_qk,fT)local cW=WpQBPz(DYbu7)return cW(TzOhP,WwYOV)end O11E["require-macros"]=function(DzAYbnQN,v,iX,xTWLsd) AHA.assert((#DzAYbnQN==2),"Expected one module name argument",(xTWLsd or DzAYbnQN))local et4qFvy=i4bMH(DzAYbnQN,v,iX,{}) AHA.assert(Jzh["string?"](et4qFvy),"module name must compile to string",( xTWLsd or DzAYbnQN)) if not uA5vZEb[et4qFvy]then local j,u3d=X1okTW(et4qFvy,1) AHA.assert(j,(et4qFvy.. " module not found."),DzAYbnQN)do end (uA5vZEb)[et4qFvy]=AHA.assert(Jzh["table?"](j(et4qFvy,u3d)),"expected macros to be table",( xTWLsd or DzAYbnQN))else end if("import-macros"==tostring(DzAYbnQN[1]))then return uA5vZEb[et4qFvy]else return k7(uA5vZEb[et4qFvy],DzAYbnQN,v,iX)end end qh0m("require-macros",{"macro-module-name"},"Load given module and use its contents as macro definitions in current scope.\nMacro module should return a table of macro functions with string keys.\nConsider using import-macros instead as it is more flexible.") local function o4u(Zk,IJlAF,PtNV,vGN) local AmjRAd75=AHA["make-scope"](Jzh.root.scope.parent)local PmbHJr={}if Jzh.root.options.requireAsInclude then AmjRAd75.specials.require=AHA["require-include"]else end for Gyrd1C,SU2WbXNN in S.parser(S["string-stream"](Zk),IJlAF)do table.insert(PmbHJr,SU2WbXNN)end for oC5=1,#PmbHJr do local ZDm if(oC5 ==#PmbHJr)then ZDm={tail=true}else ZDm={nval=0}end;Jzh["propagate-options"](PtNV,ZDm) AHA.compile1(PmbHJr[oC5],AmjRAd75,vGN,ZDm)end;return nil end local function XB(EauWm_Mc,hF5AiG,CaPHn8,kR,DeA)Jzh.root.scope.includes[kR]="fnl/loading"local dKC3Vzv do local cN=assert(io.open(CaPHn8)) local function lDwx(Ki,...)cN:close()if Ki then return...else return error(...,0)end end;local function oA() return assert(cN:read("*all")):gsub("[\13\n]*$","")end dKC3Vzv=lDwx(_G.xpcall(oA,(package.loaded.fennel or debug).traceback))end local G=Jzh.expr(("require(\""..kR.."\")"),"statement") local qx6M=("package.preload[%q]"):format(kR) local q43=(qx6M.." = "..qx6M.." or function(...)")local CEjbsJ,XL={},{}AHA.emit(CEjbsJ,q43,EauWm_Mc) AHA.emit(CEjbsJ,XL)AHA.emit(CEjbsJ,"end",EauWm_Mc)for cxdelc,YxaPQgT in ipairs(CEjbsJ)do table.insert(Jzh.root.chunk,YxaPQgT)end if DeA then o4u(dKC3Vzv,CaPHn8,hF5AiG,XL)else AHA.emit(XL,dKC3Vzv,EauWm_Mc)end;Jzh.root.scope.includes[kR]=G;return G end local function l38u_I3T(lZT0q,PSOCXW8,mJ47Xs,pttAKe) if (Jzh.root.scope.includes[lZT0q]=="fnl/loading")then AHA.assert(mJ47Xs,"circular include detected",pttAKe)return mJ47Xs(PSOCXW8)else return nil end end O11E.include=function(Qpnu6Q,BS_TVtF,AsPHBFDJ,Aedxu) AHA.assert((#Qpnu6Q==2),"expected one argument",Qpnu6Q)local oUneh9 do local Lz_usB,ljsvn5Qz=pcall(i4bMH,Qpnu6Q,BS_TVtF,AsPHBFDJ,Aedxu) if((Lz_usB==true)and(nil~=ljsvn5Qz))then local GgNU6=ljsvn5Qz oUneh9=Jzh.expr(string.format("%q",GgNU6),"literal")elseif true then local NPz6M=Lz_usB oUneh9=(AHA.compile1(Qpnu6Q[2],BS_TVtF,AsPHBFDJ,{nval=1}))[1]else oUneh9=nil end end if((oUneh9.type~="literal")or ((oUneh9[1]):byte()~=34))then if Aedxu.fallback then return Aedxu.fallback(oUneh9)else return AHA.assert(false,"module name must be string literal",Qpnu6Q)end else local sY=WpQBPz(("return ".. oUneh9[1]))() local eD2N=Jzh.root.options["module-name"]local lHWMtRj;Jzh.root.options["module-name"]=sY;lHWMtRj=nil local ooD_xGt local function b()local Zb2=Wd1a_i(sY) if(nil~=Zb2)then local NWZMp=Zb2 return XB(Qpnu6Q,Aedxu,NWZMp,sY,true)elseif true then local pYT=Zb2;local tZED=Wd1a_i(sY,package.path) if tZED then return XB(Qpnu6Q,Aedxu,tZED,sY,false)elseif Aedxu.fallback then return Aedxu.fallback(oUneh9)else return AHA.assert(false,( "module not found "..sY),Qpnu6Q)end else return nil end end ooD_xGt=( ( Jzh["member?"](sY,(Jzh.root.options.skipInclude or{}))and Aedxu.fallback(oUneh9,true))or l38u_I3T(sY,oUneh9,Aedxu.fallback,Qpnu6Q)or Jzh.root.scope.includes[sY]or b())Jzh.root.options["module-name"]=eD2N;return ooD_xGt end end qh0m("include",{"module-name-literal"},"Like require but load the target module during compilation and embed it in the\nLua output. The module must be a string literal and resolvable at compile time.") local function ti5b(tNb4,CUAxP,HX6Kb86)local LIG=gwje(tNb4,CUAxP,HX6Kb86) local oLlSHKfJ=Jzh.copy(Jzh.root.options) oLlSHKfJ.scope=AHA["make-scope"](AHA.scopes.compiler)oLlSHKfJ.allowedGlobals=V(LIG)return assert(WpQBPz(AHA.compile(tNb4,oLlSHKfJ),LhQHR(LIG)),oLlSHKfJ["module-name"],tNb4.filename)()end O11E.macros=function(bpr7l0,EMna5yqq,d6Ctwa) AHA.assert(((#bpr7l0 ==2)and Jzh["table?"](bpr7l0[2])),"Expected one table argument",bpr7l0) return k7(ti5b(bpr7l0[2],EMna5yqq,d6Ctwa),bpr7l0,EMna5yqq,d6Ctwa)end qh0m("macros",{"{:macro-name-1 (fn [...] ...) ... :macro-name-N macro-body-N}"},"Define all functions in the given table as macros local to the current scope.") O11E["eval-compiler"]=function(VYLNRk,_yp,k8)local l=VYLNRk[1]VYLNRk[1]=Jzh.sym("do") local fdIV=ti5b(VYLNRk,_yp,k8)do end(VYLNRk)[1]=l;return fdIV end qh0m("eval-compiler",{"..."},"Evaluate the body at compile-time. Use the macro system instead if possible.",true) return {doc=YMl,["current-global-names"]=V,["load-code"]=WpQBPz,["macro-loaded"]=uA5vZEb,["macro-searchers"]=Maun0kCg,["make-compiler-env"]=gwje,["search-module"]=Wd1a_i,["make-searcher"]=ur,["wrap-env"]=LhQHR}end package.preload["fennel.compiler"]=package.preload["fennel.compiler"]or function(...) local c=require("fennel.utils")local o=require("fennel.parser") local yK=require("fennel.friend")local MAKgn=(table.unpack or _G.unpack)local x9FArgz={} local function GmcCQbi(cx5uK)local uo=(cx5uK or x9FArgz.global)local ZAia0D6l;if uo then ZAia0D6l=((uo.depth or 0)+1)else ZAia0D6l=0 end return {includes=setmetatable({},{__index=(uo and uo.includes)}),macros=setmetatable({},{__index=( uo and uo.macros)}),manglings=setmetatable({},{__index=(uo and uo.manglings)}),specials=setmetatable({},{__index=(uo and uo.specials)}),symmeta=setmetatable({},{__index=( uo and uo.symmeta)}),unmanglings=setmetatable({},{__index=(uo and uo.unmanglings)}),gensyms=setmetatable({},{__index=(uo and uo.gensyms)}),autogensyms=setmetatable({},{__index=(uo and uo.autogensyms)}),vararg=( uo and uo.vararg),depth=ZAia0D6l,hashfn=(uo and uo.hashfn),refedglobals={},parent=uo}end local function jtHWFj7(DjicYU,aX)local griKc if("table"==type(DjicYU))then griKc=DjicYU else griKc={}end;local oPlKdB=getmetatable(DjicYU) local ithf=( (oPlKdB and oPlKdB.filename)or griKc.filename or"unknown") local EOxrAqR=((oPlKdB and oPlKdB.line)or griKc.line or"?") local W4MTO=((oPlKdB and oPlKdB.col)or griKc.col or"?") local uzkTxapi=tostring((c["sym?"](griKc[1])or griKc[1]or"()"))return string.format("%s:%s:%s Compile error in '%s': %s",ithf,EOxrAqR,W4MTO,uzkTxapi,aX)end local function NV(m2e,Id1VK,j2NNb,kiRfka8) if not m2e then local uteD3RU=(c.root.options or{}) local _yAm0f0Y=uteD3RU["source"]local UF=uteD3RU["unfriendly"] local gV3vGZ4X=uteD3RU["error-pinpoint"]local NGrp;if next(c["ast-source"](j2NNb))then NGrp=j2NNb else NGrp=(kiRfka8 or{})end if(nil== c.hook("assert-compile",m2e,Id1VK,NGrp,c.root.reset))then c.root.reset() if (UF or not yK or not _G.io or not _G.io.read)then error(jtHWFj7(NGrp,Id1VK),0)else yK["assert-compile"](m2e,Id1VK,NGrp,_yAm0f0Y,{["error-pinpoint"]=gV3vGZ4X})end else end else end;return m2e end;x9FArgz.global=GmcCQbi()x9FArgz.global.vararg=true x9FArgz.compiler=GmcCQbi(x9FArgz.global)x9FArgz.macro=x9FArgz.global local tQX2JQ3q={["\7"]="\\a",["\8"]="\\b",["\9"]="\\t",["\n"]="n",["\11"]="\\v",["\12"]="\\f"} local function iIyPus(tQpA)local function V(x799K)return("\\"..x799K:byte())end;return string.gsub(string.gsub(string.format("%q",tQpA),".",tQX2JQ3q),"[\128-\255]",V)end local function UDoYdga(jZ) if c["valid-lua-identifier?"](jZ)then return jZ else local function OmVG8lf(CgZsZTK)return string.format("_%02x",CgZsZTK:byte())end;return("__fnl_global__".. jZ:gsub("[^%w]",OmVG8lf))end end local function u_URqAyP(lEc)local YaAvE=string.match(lEc,"^__fnl_global__(.*)$") if (nil~=YaAvE)then local yf3=YaAvE;local Bcs;local function vh77(Gq) return string.char(tonumber(Gq:sub(2),16))end Bcs=string.gsub(yf3,"_[%da-f][%da-f]",vh77)return Bcs elseif true then local IhMea=YaAvE;return lEc else return nil end end;local nxZZC=nil;local function DMZ(JfP4Aohm)return (not nxZZC or c["member?"](JfP4Aohm,nxZZC))end local function ebmBtt(F6q,K0,WSUP53i,f8pHDS)if ( WSUP53i.unmanglings[K0]and not WSUP53i.gensyms[K0])then return ebmBtt(F6q,(F6q..f8pHDS),WSUP53i,(f8pHDS+1))else return K0 end end local function F6(r1hTTS,WaWBNK,e4qPh_,M1) NV(not c["multi-sym?"](r1hTTS),("unexpected multi symbol "..r1hTTS),e4qPh_)local Fe if ((c["lua-keywords"])[r1hTTS]or r1hTTS:match("^%d"))then Fe=("_"..r1hTTS)else Fe=r1hTTS end;local RK local function p(eXTmm)return string.format("_%02x",eXTmm:byte())end RK=string.gsub(string.gsub(Fe,"-","_"),"[^%w_]",p)local P0VL2vrZ=ebmBtt(RK,RK,WaWBNK,0)do end (WaWBNK.unmanglings)[P0VL2vrZ]=r1hTTS;do local xy0J=(M1 or WaWBNK.manglings)do end (xy0J)[r1hTTS]=P0VL2vrZ end;return P0VL2vrZ end local function Mcv4zh(kN,Je,lbp) for qc,SnR in pairs(Je)do NV(not kN.refedglobals[SnR],("use of global ".. qc.." is aliased by a local"),lbp)do end(kN.manglings)[qc]=SnR end;return nil end local function Aanl(uREm,sYd4vJv) local Tp=(sYd4vJv.manglings[uREm[1]]or UDoYdga(uREm[1])) for dL=2,#uREm do if c["valid-lua-identifier?"](uREm[dL])then if( uREm["multi-sym-method-call"]and(dL==#uREm))then Tp=(Tp.. ":"..uREm[dL])else Tp=(Tp.."."..uREm[dL])end else Tp=(Tp.."["..iIyPus(uREm[dL]).. "]")end end;return Tp end local function VvZoG6()c.root.scope["gensym-append"]=( (c.root.scope["gensym-append"]or 0)+1)return ( "_"..c.root.scope["gensym-append"].."_")end local function AjTaG(cv,YQ,f) local yDOTU=((YQ or"")..VvZoG6().. (f or""))while cv.unmanglings[yDOTU]do yDOTU=((YQ or"")..VvZoG6().. (f or""))end cv.unmanglings[yDOTU]=(YQ or true)do end(cv.gensyms)[yDOTU]=true;return yDOTU end local function oJv55vky(xs3HoM,CPt)xs3HoM[1]=CPt;local x25XhH=table.remove(xs3HoM) local gdJk_iMs=table.remove(xs3HoM) local I=((xs3HoM["multi-sym-method-call"]and":")or".") table.insert(xs3HoM,(gdJk_iMs..I..x25XhH))return table.concat(xs3HoM,".")end local function ecvFfcqw(sRldmKE8,J4S6iy)local e5Hi=c["multi-sym?"](sRldmKE8) if(nil~=e5Hi)then local glqD=e5Hi;return oJv55vky(glqD,ecvFfcqw(glqD[1],J4S6iy))elseif true then local HndLYXOs=e5Hi;local function hFfZ() local _jVsRVIY=AjTaG(J4S6iy,sRldmKE8:sub(1,(-2)),"auto")do end(J4S6iy.autogensyms)[sRldmKE8]=_jVsRVIY return _jVsRVIY end;return( J4S6iy.autogensyms[sRldmKE8]or hFfZ())else return nil end end local function giqEv(eF5jN,BMmEIl,co,tZQ)local YplB=tostring(eF5jN)local iuAKOawa do local Y9ajqGT8=tZQ;if(nil~=Y9ajqGT8)then Y9ajqGT8=(Y9ajqGT8)["macro?"]else end;iuAKOawa=Y9ajqGT8 end NV(not YplB:find("&"),"invalid character: &",eF5jN) NV(not YplB:find("^%."),"invalid character: .",eF5jN) NV(not(BMmEIl.specials[YplB]or (not iuAKOawa and BMmEIl.macros[YplB])),("local %s was overshadowed by a special form or macro"):format(YplB),co)return NV(not c["quoted?"](eF5jN),string.format("macro tried to bind %s without gensym",YplB),eF5jN)end local function DX(z,l,ed,WipOX,GepSAE)giqEv(z,ed,WipOX)local fVwL=tostring(z) NV(not c["multi-sym?"](fVwL),( "unexpected multi symbol "..fVwL),WipOX)do end(ed.symmeta)[fVwL]=l;return F6(fVwL,ed,WipOX,GepSAE)end local function Ymc47(Vdr5iR,kSubWX2,TJFj3cha) if not TJFj3cha.hashfn then return nil elseif(Vdr5iR=="$")then return"$1"elseif kSubWX2 then if(kSubWX2 and (kSubWX2[1]=="$"))then kSubWX2[1]="$1"else end;return table.concat(kSubWX2,".")else return nil end end local function KU1TZ(epF2,e,w2rce)c.hook("symbol-to-expression",epF2,e,w2rce) local SoO=epF2[1]local cvVVLDX=c["multi-sym?"](SoO) local adFOO=(Ymc47(SoO,cvVVLDX,e)or SoO)local hq=(cvVVLDX or{adFOO})local qhy=( ((1 <#hq)and"expression")or"sym") local YqkxFq=e.manglings[hq[1]]if(YqkxFq and e.symmeta[hq[1]])then e.symmeta[hq[1]]["used"]=true else end NV(not e.macros[hq[1]],"tried to reference a macro without calling it",epF2) NV((not e.specials[hq[1]]or("require"==hq[1])),"tried to reference a special form without calling it",epF2) NV((not w2rce or YqkxFq or("_ENV"==hq[1])or DMZ(hq[1])),( "unknown identifier: "..tostring(hq[1])),epF2)if(nxZZC and not YqkxFq and e.parent)then e.parent.refedglobals[hq[1]]=true else end return c.expr(Aanl(hq,e),qhy)end local function InEPht(RC1zQfj,tw7,Ia) if(type(tw7)=="table")then return table.insert(RC1zQfj,tw7)else return table.insert(RC1zQfj,{ast=Ia,leaf=tw7})end end local function dAkZ7FOa(cO) if cO.leaf then return cO elseif ( (3 <=#cO)and(cO[(#cO-2)].leaf=="do")and not cO[(#cO-1)].leaf and( cO[#cO].leaf=="end"))then local Yim0=dAkZ7FOa(cO[(#cO-1)])local KEgdrEJ={ast=cO.ast} for cvE=1,(#cO-3) do table.insert(KEgdrEJ,dAkZ7FOa(cO[cvE]))end for _ZaOK6=1,#Yim0 do table.insert(KEgdrEJ,Yim0[_ZaOK6])end;return KEgdrEJ else return c.map(cO,dAkZ7FOa)end end local function kUC7(cnMUYf,DAbP_w) local function IsHliUG5(x63,GD,tZs8,ECC)local Wj8Z7ASU=tZs8 if x63.leaf then GD[Wj8Z7ASU]=((GD[Wj8Z7ASU]or"").." "..x63.leaf)else for DPBC0cEs,L3OyIW5 in ipairs(x63)do if(L3OyIW5.leaf or(0 <#L3OyIW5))then local IA=c["ast-source"](L3OyIW5.ast)if(ECC==IA.filename)then Wj8Z7ASU=math.max(Wj8Z7ASU,(IA.line or 0))else end Wj8Z7ASU=IsHliUG5(L3OyIW5,GD,Wj8Z7ASU,ECC)else end end end;return Wj8Z7ASU end;local onTM6={} local wmHyhAu=IsHliUG5(cnMUYf,onTM6,1,DAbP_w.filename)for ZXlUnI=1,wmHyhAu do if(onTM6[ZXlUnI]==nil)then onTM6[ZXlUnI]=""else end end return table.concat(onTM6,"\n")end local function PkKH(Yzt3,A3nycin,_AEiRFt,jA) if A3nycin.leaf then local DthGg=c["ast-source"](A3nycin.ast) local nAh1PSh=DthGg["filename"]local U3vmAAPu=DthGg["line"] table.insert(Yzt3,{nAh1PSh,U3vmAAPu})return A3nycin.leaf else local QSzXH do local CD7qMWN=_AEiRFt if(CD7qMWN==true)then QSzXH=" "elseif (CD7qMWN==false)then QSzXH=""elseif(CD7qMWN==_AEiRFt)then QSzXH=_AEiRFt elseif(CD7qMWN==nil)then QSzXH=""else QSzXH=nil end end local function ruKe(n3vXu) if(n3vXu.leaf or(0 <#n3vXu))then local dFb_c5=PkKH(Yzt3,n3vXu,QSzXH,(jA+1))if(0 ]+$")end local K9fi_VCl={{["min-byte"]=0,["max-byte"]=127,["min-code"]=0,["max-code"]=127,len=1},{["min-byte"]=192,["max-byte"]=223,["min-code"]=128,["max-code"]=2047,len=2},{["min-byte"]=224,["max-byte"]=239,["min-code"]=2048,["max-code"]=65535,len=3},{["min-byte"]=240,["max-byte"]=247,["min-code"]=65536,["max-code"]=1114111,len=4}} local function xknmF(nM1Z9cl) local function w(UcyAum,dYsjmqdo)local k=K9fi_VCl;local Ur_nfNU=string.byte(UcyAum,dYsjmqdo)local YF8Nredr do local tyx_Qk=nil for urZP,ayXNm in qvEX8(k)do if tyx_Qk then break end tyx_Qk=(Ur_nfNU and (function(Iq9D_7Yo,OMesxhmz,Q9BrppG)return(Iq9D_7Yo<=OMesxhmz)and (OMesxhmz<=Q9BrppG)end)(ayXNm["min-byte"],Ur_nfNU,ayXNm["max-byte"])and ayXNm)end;YF8Nredr=tyx_Qk end;local wFF9q_B local function KLNU()local D9E4D;if YF8Nredr then D9E4D=(Ur_nfNU-YF8Nredr["min-byte"])else D9E4D=nil end for hhiP24bV=(dYsjmqdo+1),( dYsjmqdo+YF8Nredr.len+-1)do local O34P1h=string.byte(UcyAum,hhiP24bV) D9E4D=(O34P1h and D9E4D and (function(M,oW2YQpc,EX1c04A) return(M<=oW2YQpc)and(oW2YQpc<=EX1c04A)end)(128,O34P1h,191)and((D9E4D*64)+ (O34P1h-128)))end;return D9E4D end;wFF9q_B=(YF8Nredr and KLNU()) if (wFF9q_B and (function(svWLk,H6V,vZece1k)return(svWLk<=H6V)and (H6V<=vZece1k)end)(YF8Nredr["min-code"],wFF9q_B,YF8Nredr["max-code"])and not (function(ZUrs,cJ9n_wGv,oNrtf_)return (ZUrs<=cJ9n_wGv)and(cJ9n_wGv<=oNrtf_)end)(55296,wFF9q_B,57343))then return YF8Nredr.len else return nil end end;local UTW=1;local hYYhQ={} while(UTW<=#nM1Z9cl)do local HtgpP=( string.find(nM1Z9cl,"[\128-\255]",UTW)or(#nM1Z9cl+1))local lyBbtwn=w(nM1Z9cl,HtgpP) table.insert(hYYhQ,string.sub(nM1Z9cl,UTW,( HtgpP+ (lyBbtwn or 0)+-1)))if(not lyBbtwn and(HtgpP<=#nM1Z9cl))then table.insert(hYYhQ,string.format("\\%03d",string.byte(nM1Z9cl,HtgpP)))else end;if lyBbtwn then UTW=(HtgpP+lyBbtwn)else UTW=(HtgpP+1)end end;return table.concat(hYYhQ)end local function Z69(biFy,Kp,o4A)local M9=B(biFy) local rzBY2CSb=((M9 <2)or (sF(Kp,"escape-newlines?")and(M9 < (Kp["line-length"]-o4A))))local eJbv;local BUVM8;if rzBY2CSb then BUVM8="\\n"else BUVM8="\n"end;local function k(Kl1YC,b8)return ("\\%03d"):format(b8:byte())end eJbv=setmetatable({["\7"]="\\a",["\8"]="\\b",["\12"]="\\f",["\11"]="\\v",["\13"]="\\r",["\9"]="\\t",["\\"]="\\\\",["\""]="\\\"",["\n"]=BUVM8},{__index=k}) local x5=("\""..biFy:gsub("[%c\\\"]",eJbv).."\"")if sF(Kp,"utf8?")then return xknmF(x5)else return x5 end end local function i(bGdXdZ,iIoI)local F do local UFS7={} for rsqKt,e in wXFw(Q)do local qIYmlZUV,ae8WUke=rsqKt,e;if ((qIYmlZUV~=nil)and(ae8WUke~=nil))then UFS7[qIYmlZUV]=ae8WUke else end end;F=UFS7 end;local qRV={level=0,appearances=_KX1(bGdXdZ,{}),seen={len=0}}for EU8,B6 in wXFw(( iIoI or{}))do F[EU8]=B6 end;for KJgE8hd,ufBK in wXFw(qRV)do F[KJgE8hd]=ufBK end;return F end local function IDjuB(cZ,maaag,Bi35M,ZjzhK)local tihCL=(Bi35M or 0)local r=(maaag or i(cZ))local ZDeC;if r.preprocess then ZDeC=r.preprocess(cZ,r)else ZDeC=cZ end;local HZlrFcs=type(ZDeC) local function O() local QD=getmetatable(ZDeC)if(nil~=QD)then return(QD).__fennelview else return QD end end if((HZlrFcs=="table")or ((HZlrFcs=="userdata")and O()))then return b81Fw(ZDeC,r,tihCL)elseif(HZlrFcs=="number")then return umz(ZDeC)else local function hcbBcU() if(ZjzhK~=nil)then return ZjzhK elseif("function"== type(r["prefer-colon?"]))then return r["prefer-colon?"](ZDeC)else return sF(r,"prefer-colon?")end end if ((HZlrFcs=="string")and JXCg(ZDeC)and hcbBcU())then return(":"..ZDeC)elseif(HZlrFcs=="string")then return Z69(ZDeC,r,tihCL)elseif (( HZlrFcs=="boolean")or(HZlrFcs=="nil"))then return tostring(ZDeC)else return ("#<"..tostring(ZDeC)..">")end end end;VPVZ=IDjuB local function xoF(OkdSp2,zO7D8g2)return VPVZ(OkdSp2,i(OkdSp2,zO7D8g2),0)end;return xoF end package.preload["fennel.utils"]=package.preload["fennel.utils"]or function(...) local ywRB55Y3=require("fennel.view")local uUc5YN="1.3.0" local function tBlx5J()return ( (nil~=_G.jit)and(type(_G.jit)=="table")and(nil~=_G.jit.on)and (nil~=_G.jit.off)and (type(_G.jit.version_num)=="number"))end local function BJLlq()local ZfJ if(_G.jit.os=="OSX")then ZfJ="macOS"else ZfJ=_G.jit.os end;return (_G.jit.version.." "..ZfJ.."/".._G.jit.arch)end local function DsnW()return ( (nil~=_G.fengari)and(type(_G.fengari)=="table")and(nil~=_G.fengari.VERSION)and (type(_G.fengari.VERSION_NUM)=="number"))end;local function f0qgvqD()return (_G.fengari.RELEASE.." (".._VERSION..")")end;local function r() if tBlx5J()then return BJLlq()elseif DsnW()then return f0qgvqD()else return("PUC ".._VERSION)end end local function IYCyS()return("Fennel "..uUc5YN.. " on "..r())end;local function rgNe(kZ) if(_G.io and _G.io.stderr)then return (_G.io.stderr):write(("--WARNING: %s\n"):format(tostring(kZ)))else return nil end end local wMf06P3R do local G,UV=pcall(require,"utf8") if ((G==true)and(nil~=UV))then local Vmk7fAg=UV;wMf06P3R=Vmk7fAg.len elseif true then local kDtxd5=G;wMf06P3R=string.len else wMf06P3R=nil end end local function dzghhciK(CR5bl6Nk,BjIQON,MLLs29h) for Umpt,NVyj in ipairs(getmetatable(CR5bl6Nk).keys)do if(CR5bl6Nk[NVyj]and not MLLs29h[NVyj])then MLLs29h[NVyj]=true table.insert(BjIQON,NVyj)else end end;for Nt3 in pairs(CR5bl6Nk)do if not MLLs29h[Nt3]then table.insert(BjIQON,Nt3)else end end;return BjIQON end local function PwmycU9j(UPRQV)local OuKZ9r5_b;local AE;do local ZXC=getmetatable(UPRQV) if(nil~=ZXC)then ZXC=(ZXC).keys else end;AE=ZXC end if AE then OuKZ9r5_b=dzghhciK(UPRQV,{},{})else local mPFtR do local cwH={}local L8qe=#cwH for w0n in pairs(UPRQV)do local Fulc=w0n;if(nil~=Fulc)then L8qe=(L8qe+1) do end(cwH)[L8qe]=Fulc else end end;mPFtR=cwH end;local function NK3P(ZEnxqQUU,vKS) return(tostring(ZEnxqQUU)* [val ...] "Thread-first macro. Take the first value and splice it into the second form as its first argument. The value of the second form is spliced into the first arg of the third, etc." (var x val) (each [_ e (ipairs [...])] (let [elt (if (list? e) (copy e) (list e))] (table.insert elt 2 x) (set x elt))) x) (fn ->>* [val ...] "Thread-last macro. Same as ->, except splices the value into the last position of each form rather than the first." (var x val) (each [_ e (ipairs [...])] (let [elt (if (list? e) (copy e) (list e))] (table.insert elt x) (set x elt))) x) (fn -?>* [val ?e ...] "Nil-safe thread-first macro. Same as -> except will short-circuit with nil when it encounters a nil value." (if (= nil ?e) val (let [el (if (list? ?e) (copy ?e) (list ?e)) tmp (gensym)] (table.insert el 2 tmp) `(let [,tmp ,val] (if (not= nil ,tmp) (-?> ,el ,...) ,tmp))))) (fn -?>>* [val ?e ...] "Nil-safe thread-last macro. Same as ->> except will short-circuit with nil when it encounters a nil value." (if (= nil ?e) val (let [el (if (list? ?e) (copy ?e) (list ?e)) tmp (gensym)] (table.insert el tmp) `(let [,tmp ,val] (if (not= ,tmp nil) (-?>> ,el ,...) ,tmp))))) (fn ?dot [tbl ...] "Nil-safe table look up. Same as . (dot), except will short-circuit with nil when it encounters a nil value in any of subsequent keys." (let [head (gensym :t) lookups `(do (var ,head ,tbl) ,head)] (each [_ k (ipairs [...])] ;; Kinda gnarly to reassign in place like this, but it emits the best lua. ;; With this impl, it emits a flat, concise, and readable set of ifs (table.insert lookups (# lookups) `(if (not= nil ,head) (set ,head (. ,head ,k))))) lookups)) (fn doto* [val ...] "Evaluate val and splice it into the first argument of subsequent forms." (assert (not= val nil) "missing subject") (let [rebind? (or (not (sym? val)) (multi-sym? val)) name (if rebind? (gensym) val) form (if rebind? `(let [,name ,val]) `(do))] (each [_ elt (ipairs [...])] (let [elt (if (list? elt) (copy elt) (list elt))] (table.insert elt 2 name) (table.insert form elt))) (table.insert form name) form)) (fn when* [condition body1 ...] "Evaluate body for side-effects only when condition is truthy." (assert body1 "expected body") `(if ,condition (do ,body1 ,...))) (fn with-open* [closable-bindings ...] "Like `let`, but invokes (v:close) on each binding after evaluating the body. The body is evaluated inside `xpcall` so that bound values will be closed upon encountering an error before propagating it." (let [bodyfn `(fn [] ,...) closer `(fn close-handlers# [ok# ...] (if ok# ... (error ... 0))) traceback `(. (or package.loaded.fennel debug) :traceback)] (for [i 1 (length closable-bindings) 2] (assert (sym? (. closable-bindings i)) "with-open only allows symbols in bindings") (table.insert closer 4 `(: ,(. closable-bindings i) :close))) `(let ,closable-bindings ,closer (close-handlers# (_G.xpcall ,bodyfn ,traceback))))) (fn extract-into [iter-tbl] (var (into iter-out found?) (values [] (copy iter-tbl))) (for [i (length iter-tbl) 2 -1] (let [item (. iter-tbl i)] (if (or (= `&into item) (= :into item)) (do (assert (not found?) "expected only one &into clause") (set found? true) (set into (. iter-tbl (+ i 1))) (table.remove iter-out i) (table.remove iter-out i))))) (assert (or (not found?) (sym? into) (table? into) (list? into)) "expected table, function call, or symbol in &into clause") (values into iter-out)) (fn collect* [iter-tbl key-expr value-expr ...] "Return a table made by running an iterator and evaluating an expression that returns key-value pairs to be inserted sequentially into the table. This can be thought of as a table comprehension. The body should provide two expressions (used as key and value) or nil, which causes it to be omitted. For example, (collect [k v (pairs {:apple \"red\" :orange \"orange\"})] (values v k)) returns {:red \"apple\" :orange \"orange\"} Supports an &into clause after the iterator to put results in an existing table. Supports early termination with an &until clause." (assert (and (sequence? iter-tbl) (<= 2 (length iter-tbl))) "expected iterator binding table") (assert (not= nil key-expr) "expected key and value expression") (assert (= nil ...) "expected 1 or 2 body expressions; wrap multiple expressions with do") (let [kv-expr (if (= nil value-expr) key-expr `(values ,key-expr ,value-expr)) (into iter) (extract-into iter-tbl)] `(let [tbl# ,into] (each ,iter (let [(k# v#) ,kv-expr] (if (and (not= k# nil) (not= v# nil)) (tset tbl# k# v#)))) tbl#))) (fn seq-collect [how iter-tbl value-expr ...] "Common part between icollect and fcollect for producing sequential tables. Iteration code only differs in using the for or each keyword, the rest of the generated code is identical." (assert (not= nil value-expr) "expected table value expression") (assert (= nil ...) "expected exactly one body expression. Wrap multiple expressions in do") (let [(into iter) (extract-into iter-tbl)] `(let [tbl# ,into] ;; believe it or not, using a var here has a pretty good performance ;; boost: https://p.hagelb.org/icollect-performance.html (var i# (length tbl#)) (,how ,iter (let [val# ,value-expr] (when (not= nil val#) (set i# (+ i# 1)) (tset tbl# i# val#)))) tbl#))) (fn icollect* [iter-tbl value-expr ...] "Return a sequential table made by running an iterator and evaluating an expression that returns values to be inserted sequentially into the table. This can be thought of as a table comprehension. If the body evaluates to nil that element is omitted. For example, (icollect [_ v (ipairs [1 2 3 4 5])] (when (not= v 3) (* v v))) returns [1 4 16 25] Supports an &into clause after the iterator to put results in an existing table. Supports early termination with an &until clause." (assert (and (sequence? iter-tbl) (<= 2 (length iter-tbl))) "expected iterator binding table") (seq-collect 'each iter-tbl value-expr ...)) (fn fcollect* [iter-tbl value-expr ...] "Return a sequential table made by advancing a range as specified by for, and evaluating an expression that returns values to be inserted sequentially into the table. This can be thought of as a range comprehension. If the body evaluates to nil that element is omitted. For example, (fcollect [i 1 10 2] (when (not= i 3) (* i i))) returns [1 25 49 81] Supports an &into clause after the range to put results in an existing table. Supports early termination with an &until clause." (assert (and (sequence? iter-tbl) (< 2 (length iter-tbl))) "expected range binding table") (seq-collect 'for iter-tbl value-expr ...)) (fn accumulate-impl [for? iter-tbl body ...] (assert (and (sequence? iter-tbl) (<= 4 (length iter-tbl))) "expected initial value and iterator binding table") (assert (not= nil body) "expected body expression") (assert (= nil ...) "expected exactly one body expression. Wrap multiple expressions with do") (let [[accum-var accum-init] iter-tbl iter (sym (if for? "for" "each"))] ; accumulate or faccumulate? `(do (var ,accum-var ,accum-init) (,iter ,[(unpack iter-tbl 3)] (set ,accum-var ,body)) ,(if (list? accum-var) (list (sym :values) (unpack accum-var)) accum-var)))) (fn accumulate* [iter-tbl body ...] "Accumulation macro. It takes a binding table and an expression as its arguments. In the binding table, the first form starts out bound to the second value, which is an initial accumulator. The rest are an iterator binding table in the format `each` takes. It runs through the iterator in each step of which the given expression is evaluated, and the accumulator is set to the value of the expression. It eventually returns the final value of the accumulator. For example, (accumulate [total 0 _ n (pairs {:apple 2 :orange 3})] (+ total n)) returns 5" (accumulate-impl false iter-tbl body ...)) (fn faccumulate* [iter-tbl body ...] "Identical to accumulate, but after the accumulator the binding table is the same as `for` instead of `each`. Like collect to fcollect, will iterate over a numerical range like `for` rather than an iterator." (accumulate-impl true iter-tbl body ...)) (fn double-eval-safe? [x type] (or (= :number type) (= :string type) (= :boolean type) (and (sym? x) (not (multi-sym? x))))) (fn partial* [f ...] "Return a function with all arguments partially applied to f." (assert f "expected a function to partially apply") (let [bindings [] args []] (each [_ arg (ipairs [...])] (if (double-eval-safe? arg (type arg)) (table.insert args arg) (let [name (gensym)] (table.insert bindings name) (table.insert bindings arg) (table.insert args name)))) (let [body (list f (unpack args))] (table.insert body _VARARG) ;; only use the extra let if we need double-eval protection (if (= 0 (length bindings)) `(fn [,_VARARG] ,body) `(let ,bindings (fn [,_VARARG] ,body)))))) (fn pick-args* [n f] "Create a function of arity n that applies its arguments to f. For example, (pick-args 2 func) expands to (fn [_0_ _1_] (func _0_ _1_))" (if (and _G.io _G.io.stderr) (_G.io.stderr:write "-- WARNING: pick-args is deprecated and will be removed in the future.\n")) (assert (and (= (type n) :number) (= n (math.floor n)) (<= 0 n)) (.. "Expected n to be an integer literal >= 0, got " (tostring n))) (let [bindings []] (for [i 1 n] (tset bindings i (gensym))) `(fn ,bindings (,f ,(unpack bindings))))) (fn pick-values* [n ...] "Evaluate to exactly n values. For example, (pick-values 2 ...) expands to (let [(_0_ _1_) ...] (values _0_ _1_))" (assert (and (= :number (type n)) (<= 0 n) (= n (math.floor n))) (.. "Expected n to be an integer >= 0, got " (tostring n))) (let [let-syms (list) let-values (if (= 1 (select "#" ...)) ... `(values ,...))] (for [i 1 n] (table.insert let-syms (gensym))) (if (= n 0) `(values) `(let [,let-syms ,let-values] (values ,(unpack let-syms)))))) (fn lambda* [...] "Function literal with nil-checked arguments. Like `fn`, but will throw an exception if a declared argument is passed in as nil, unless that argument's name begins with a question mark." (let [args [...] has-internal-name? (sym? (. args 1)) arglist (if has-internal-name? (. args 2) (. args 1)) docstring-position (if has-internal-name? 3 2) has-docstring? (and (< docstring-position (length args)) (= :string (type (. args docstring-position)))) arity-check-position (- 4 (if has-internal-name? 0 1) (if has-docstring? 0 1)) empty-body? (< (length args) arity-check-position)] (fn check! [a] (if (table? a) (each [_ a (pairs a)] (check! a)) (let [as (tostring a)] (and (not (as:match "^?")) (not= as "&") (not= as "_") (not= as "...") (not= as "&as"))) (table.insert args arity-check-position `(_G.assert (not= nil ,a) ,(: "Missing argument %s on %s:%s" :format (tostring a) (or a.filename :unknown) (or a.line "?")))))) (assert (= :table (type arglist)) "expected arg list") (each [_ a (ipairs arglist)] (check! a)) (if empty-body? (table.insert args (sym :nil))) `(fn ,(unpack args)))) (fn macro* [name ...] "Define a single macro." (assert (sym? name) "expected symbol for macro name") (local args [...]) `(macros {,(tostring name) (fn ,(unpack args))})) (fn macrodebug* [form return?] "Print the resulting form after performing macroexpansion. With a second argument, returns expanded form as a string instead of printing." (let [handle (if return? `do `print)] `(,handle ,(view (macroexpand form _SCOPE))))) (fn import-macros* [binding1 module-name1 ...] "Bind a table of macros from each macro module according to a binding form. Each binding form can be either a symbol or a k/v destructuring table. Example: (import-macros mymacros :my-macros ; bind to symbol {:macro1 alias : macro2} :proj.macros) ; import by name" (assert (and binding1 module-name1 (= 0 (% (select "#" ...) 2))) "expected even number of binding/modulename pairs") (for [i 1 (select "#" binding1 module-name1 ...) 2] ;; delegate the actual loading of the macros to the require-macros ;; special which already knows how to set up the compiler env and stuff. ;; this is weird because require-macros is deprecated but it works. (let [(binding modname) (select i binding1 module-name1 ...) scope (get-scope) ;; if the module-name is an expression (and not just a string) we ;; patch our expression to have the correct source filename so ;; require-macros can pass it down when resolving the module-name. expr `(import-macros ,modname) filename (if (list? modname) (. modname 1 :filename) :unknown) _ (tset expr :filename filename) macros* (_SPECIALS.require-macros expr scope {} binding)] (if (sym? binding) ;; bind whole table of macros to table bound to symbol (tset scope.macros (. binding 1) macros*) ;; 1-level table destructuring for importing individual macros (table? binding) (each [macro-name [import-key] (pairs binding)] (assert (= :function (type (. macros* macro-name))) (.. "macro " macro-name " not found in module " (tostring modname))) (tset scope.macros import-key (. macros* macro-name)))))) nil) {:-> ->* :->> ->>* :-?> -?>* :-?>> -?>>* :?. ?dot :doto doto* :when when* :with-open with-open* :collect collect* :icollect icollect* :fcollect fcollect* :accumulate accumulate* :faccumulate faccumulate* :partial partial* :lambda lambda* :λ lambda* :pick-args pick-args* :pick-values pick-values* :macro macro* :macrodebug macrodebug* :import-macros import-macros*} ]===],{env=KPHc6Aeq,scope=Mm.scopes.compiler,useMetadata=true,filename="src/fennel/macros.fnl",moduleName=IGiruNd})local Z53B for PtID,j in pairs(kB6a)do Mm.scopes.global.macros[PtID]=j end;Z53B=nil local _5pPpX=K([===[;;; Pattern matching ;; This is separated out so we can use the "core" macros during the ;; implementation of pattern matching. (fn copy [t] (collect [k v (pairs t)] k v)) (fn with [opts k] (doto (copy opts) (tset k true))) (fn without [opts k] (doto (copy opts) (tset k nil))) (fn case-values [vals pattern unifications case-pattern opts] (let [condition `(and) bindings []] (each [i pat (ipairs pattern)] (let [(subcondition subbindings) (case-pattern [(. vals i)] pat unifications (without opts :multival?))] (table.insert condition subcondition) (icollect [_ b (ipairs subbindings) &into bindings] b))) (values condition bindings))) (fn case-table [val pattern unifications case-pattern opts] (let [condition `(and (= (_G.type ,val) :table)) bindings []] (each [k pat (pairs pattern)] (if (= pat `&) (let [rest-pat (. pattern (+ k 1)) rest-val `(select ,k ((or table.unpack _G.unpack) ,val)) subcondition (case-table `(pick-values 1 ,rest-val) rest-pat unifications case-pattern (without opts :multival?))] (if (not (sym? rest-pat)) (table.insert condition subcondition)) (assert (= nil (. pattern (+ k 2))) "expected & rest argument before last parameter") (table.insert bindings rest-pat) (table.insert bindings [rest-val])) (= k `&as) (do (table.insert bindings pat) (table.insert bindings val)) (and (= :number (type k)) (= `&as pat)) (do (assert (= nil (. pattern (+ k 2))) "expected &as argument before last parameter") (table.insert bindings (. pattern (+ k 1))) (table.insert bindings val)) ;; don't process the pattern right after &/&as; already got it (or (not= :number (type k)) (and (not= `&as (. pattern (- k 1))) (not= `& (. pattern (- k 1))))) (let [subval `(. ,val ,k) (subcondition subbindings) (case-pattern [subval] pat unifications (without opts :multival?))] (table.insert condition subcondition) (icollect [_ b (ipairs subbindings) &into bindings] b)))) (values condition bindings))) (fn case-guard [vals condition guards unifications case-pattern opts] (if (= 0 (length guards)) (case-pattern vals condition unifications opts) (let [(pcondition bindings) (case-pattern vals condition unifications opts) condition `(and ,(unpack guards))] (values `(and ,pcondition (let ,bindings ,condition)) bindings)))) (fn symbols-in-pattern [pattern] "gives the set of symbols inside a pattern" (if (list? pattern) (let [result {}] (each [_ child-pattern (ipairs pattern)] (collect [name symbol (pairs (symbols-in-pattern child-pattern)) &into result] name symbol)) result) (sym? pattern) (if (and (not= pattern `or) (not= pattern `where) (not= pattern `?) (not= pattern `nil)) {(tostring pattern) pattern} {}) (= (type pattern) :table) (let [result {}] (each [key-pattern value-pattern (pairs pattern)] (collect [name symbol (pairs (symbols-in-pattern key-pattern)) &into result] name symbol) (collect [name symbol (pairs (symbols-in-pattern value-pattern)) &into result] name symbol)) result) {})) (fn symbols-in-every-pattern [pattern-list infer-unification?] "gives a list of symbols that are present in every pattern in the list" (let [?symbols (accumulate [?symbols nil _ pattern (ipairs pattern-list)] (let [in-pattern (symbols-in-pattern pattern)] (if ?symbols (do (each [name symbol (pairs ?symbols)] (when (not (. in-pattern name)) (tset ?symbols name nil))) ?symbols) in-pattern)))] (icollect [_ symbol (pairs (or ?symbols {}))] (if (not (and infer-unification? (in-scope? symbol))) symbol)))) (fn case-or [vals pattern guards unifications case-pattern opts] (let [pattern [(unpack pattern 2)] bindings (symbols-in-every-pattern pattern opts.infer-unification?)] ;; TODO opts.infer-unification instead of opts.unification? (if (= 0 (length bindings)) ;; no bindings special case generates simple code (let [condition (icollect [i subpattern (ipairs pattern) &into `(or)] (let [(subcondition subbindings) (case-pattern vals subpattern unifications opts)] subcondition))] (values (if (= 0 (length guards)) condition `(and ,condition ,(unpack guards))) [])) ;; case with bindings is handled specially, and returns three values instead of two (let [matched? (gensym :matched?) bindings-mangled (icollect [_ binding (ipairs bindings)] (gensym (tostring binding))) pre-bindings `(if)] (each [i subpattern (ipairs pattern)] (let [(subcondition subbindings) (case-guard vals subpattern guards {} case-pattern opts)] (table.insert pre-bindings subcondition) (table.insert pre-bindings `(let ,subbindings (values true ,(unpack bindings)))))) (values matched? [`(,(unpack bindings)) `(values ,(unpack bindings-mangled))] [`(,matched? ,(unpack bindings-mangled)) pre-bindings]))))) (fn case-pattern [vals pattern unifications opts top-level?] "Take the AST of values and a single pattern and returns a condition to determine if it matches as well as a list of bindings to introduce for the duration of the body if it does match." ;; This function returns the following values (multival): ;; a "condition", which is an expression that determines whether the ;; pattern should match, ;; a "bindings", which bind all of the symbols used in a pattern ;; an optional "pre-bindings", which is a list of bindings that happen ;; before the condition and bindings are evaluated. These should only ;; come from a (case-or). In this case there should be no recursion: ;; the call stack should be case-condition > case-pattern > case-or ;; ;; Here are the expected flags in the opts table: ;; :infer-unification? boolean - if the pattern should guess when to unify (ie, match -> true, case -> false) ;; :multival? boolean - if the pattern can contain multivals (in order to disallow patterns like [(1 2)]) ;; :in-where? boolean - if the pattern is surrounded by (where) (where opts into more pattern features) ;; :legacy-guard-allowed? boolean - if the pattern should allow `(a ? b) patterns ;; we have to assume we're matching against multiple values here until we ;; know we're either in a multi-valued clause (in which case we know the # ;; of vals) or we're not, in which case we only care about the first one. (let [[val] vals] (if (and (sym? pattern) (or (= pattern `nil) (and opts.infer-unification? (in-scope? pattern) (not= pattern `_)) (and opts.infer-unification? (multi-sym? pattern) (in-scope? (. (multi-sym? pattern) 1))))) (values `(= ,val ,pattern) []) ;; unify a local we've seen already (and (sym? pattern) (. unifications (tostring pattern))) (values `(= ,(. unifications (tostring pattern)) ,val) []) ;; bind a fresh local (sym? pattern) (let [wildcard? (: (tostring pattern) :find "^_")] (if (not wildcard?) (tset unifications (tostring pattern) val)) (values (if (or wildcard? (string.find (tostring pattern) "^?")) true `(not= ,(sym :nil) ,val)) [pattern val])) ;; opt-in unify with (=) (and (list? pattern) (= (. pattern 1) `=) (sym? (. pattern 2))) (let [bind (. pattern 2)] (assert-compile (= 2 (length pattern)) "(=) should take only one argument" pattern) (assert-compile (not opts.infer-unification?) "(=) cannot be used inside of match" pattern) (assert-compile opts.in-where? "(=) must be used in (where) patterns" pattern) (assert-compile (and (sym? bind) (not= bind `nil) "= has to bind to a symbol" bind)) (values `(= ,val ,bind) [])) ;; where-or clause (and (list? pattern) (= (. pattern 1) `where) (list? (. pattern 2)) (= (. pattern 2 1) `or)) (do (assert-compile top-level? "can't nest (where) pattern" pattern) (case-or vals (. pattern 2) [(unpack pattern 3)] unifications case-pattern (with opts :in-where?))) ;; where clause (and (list? pattern) (= (. pattern 1) `where)) (do (assert-compile top-level? "can't nest (where) pattern" pattern) (case-guard vals (. pattern 2) [(unpack pattern 3)] unifications case-pattern (with opts :in-where?))) ;; or clause (not allowed on its own) (and (list? pattern) (= (. pattern 1) `or)) (do (assert-compile top-level? "can't nest (or) pattern" pattern) ;; This assertion can be removed to make patterns more permissive (assert-compile false "(or) must be used in (where) patterns" pattern) (case-or vals pattern [] unifications case-pattern opts)) ;; guard clause (and (list? pattern) (= (. pattern 2) `?)) (do (assert-compile opts.legacy-guard-allowed? "legacy guard clause not supported in case" pattern) (case-guard vals (. pattern 1) [(unpack pattern 3)] unifications case-pattern opts)) ;; multi-valued patterns (represented as lists) (list? pattern) (do (assert-compile opts.multival? "can't nest multi-value destructuring" pattern) (case-values vals pattern unifications case-pattern opts)) ;; table patterns (= (type pattern) :table) (case-table val pattern unifications case-pattern opts) ;; literal value (values `(= ,val ,pattern) [])))) (fn add-pre-bindings [out pre-bindings] "Decide when to switch from the current `if` AST to a new one" (if pre-bindings ;; `out` no longer needs to grow. ;; Instead, a new tail `if` AST is introduced, which is where the rest of ;; the clauses will get appended. This way, all future clauses have the ;; pre-bindings in scope. (let [tail `(if)] (table.insert out true) (table.insert out `(let ,pre-bindings ,tail)) tail) ;; otherwise, keep growing the current `if` AST. out)) (fn case-condition [vals clauses match?] "Construct the actual `if` AST for the given match values and clauses." ;; root is the original `if` AST. ;; out is the `if` AST that is currently being grown. (let [root `(if)] (faccumulate [out root i 1 (length clauses) 2] (let [pattern (. clauses i) body (. clauses (+ i 1)) (condition bindings pre-bindings) (case-pattern vals pattern {} {:multival? true :infer-unification? match? :legacy-guard-allowed? match?} true) out (add-pre-bindings out pre-bindings)] ;; grow the `if` AST by one extra condition (table.insert out condition) (table.insert out `(let ,bindings ,body)) out)) root)) (fn count-case-multival [pattern] "Identify the amount of multival values that a pattern requires." (if (and (list? pattern) (= (. pattern 2) `?)) (count-case-multival (. pattern 1)) (and (list? pattern) (= (. pattern 1) `where)) (count-case-multival (. pattern 2)) (and (list? pattern) (= (. pattern 1) `or)) (accumulate [longest 0 _ child-pattern (ipairs pattern)] (math.max longest (count-case-multival child-pattern))) (list? pattern) (length pattern) 1)) (fn case-val-syms [clauses] "What is the length of the largest multi-valued clause? return a list of that many gensyms." (let [patterns (fcollect [i 1 (length clauses) 2] (. clauses i)) sym-count (accumulate [longest 0 _ pattern (ipairs patterns)] (math.max longest (count-case-multival pattern)))] (fcollect [i 1 sym-count &into (list)] (gensym)))) (fn case-impl [match? val ...] "The shared implementation of case and match." (assert (not= val nil) "missing subject") (assert (= 0 (math.fmod (select :# ...) 2)) "expected even number of pattern/body pairs") (assert (not= 0 (select :# ...)) "expected at least one pattern/body pair") (let [clauses [...] vals (case-val-syms clauses)] ;; protect against multiple evaluation of the value, bind against as ;; many values as we ever match against in the clauses. (list `let [vals val] (case-condition vals clauses match?)))) (fn case* [val ...] "Perform pattern matching on val. See reference for details. Syntax: (case data-expression pattern body (where pattern guards*) body (or pattern patterns*) body (where (or pattern patterns*) guards*) body ;; legacy: (pattern ? guards*) body)" (case-impl false val ...)) (fn match* [val ...] "Perform pattern matching on val, automatically unifying on variables in local scope. See reference for details. Syntax: (match data-expression pattern body (where pattern guards*) body (or pattern patterns*) body (where (or pattern patterns*) guards*) body ;; legacy: (pattern ? guards*) body)" (case-impl true val ...)) (fn case-try-step [how expr else pattern body ...] (if (= nil pattern body) expr ;; unlike regular match, we can't know how many values the value ;; might evaluate to, so we have to capture them all in ... via IIFE ;; to avoid double-evaluation. `((fn [...] (,how ... ,pattern ,(case-try-step how body else ...) ,(unpack else))) ,expr))) (fn case-try-impl [how expr pattern body ...] (let [clauses [pattern body ...] last (. clauses (length clauses)) catch (if (= `catch (and (= :table (type last)) (. last 1))) (let [[_ & e] (table.remove clauses)] e) ; remove `catch sym [`_# `...])] (assert (= 0 (math.fmod (length clauses) 2)) "expected every pattern to have a body") (assert (= 0 (math.fmod (length catch) 2)) "expected every catch pattern to have a body") (case-try-step how expr catch (unpack clauses)))) (fn case-try* [expr pattern body ...] "Perform chained pattern matching for a sequence of steps which might fail. The values from the initial expression are matched against the first pattern. If they match, the first body is evaluated and its values are matched against the second pattern, etc. If there is a (catch pat1 body1 pat2 body2 ...) form at the end, any mismatch from the steps will be tried against these patterns in sequence as a fallback just like a normal match. If there is no catch, the mismatched values will be returned as the value of the entire expression." (case-try-impl `case expr pattern body ...)) (fn match-try* [expr pattern body ...] "Perform chained pattern matching for a sequence of steps which might fail. The values from the initial expression are matched against the first pattern. If they match, the first body is evaluated and its values are matched against the second pattern, etc. If there is a (catch pat1 body1 pat2 body2 ...) form at the end, any mismatch from the steps will be tried against these patterns in sequence as a fallback just like a normal match. If there is no catch, the mismatched values will be returned as the value of the entire expression." (case-try-impl `match expr pattern body ...)) {:case case* :case-try case-try* :match match* :match-try match-try*} ]===],{env=KPHc6Aeq,scope=Mm.scopes.compiler,allowedGlobals=false,useMetadata=true,filename="src/fennel/match.fnl",moduleName=IGiruNd}) for aP,oUuAO4X6 in pairs(_5pPpX)do Mm.scopes.global.macros[aP]=oUuAO4X6 end;package.preload[IGiruNd]=nil end;local iJYISVW=FkyzO local function K(q2lIy) is_passed,out=pcall(function()return iJYISVW.eval(q2lIy)end,function(n62)return n62 end)lua=""out=tostring(out)preview=out:gsub("\n.*","")if is_passed then lua=iJYISVW.compileString(q2lIy)end return{is_passed=is_passed,preview=preview,out=out,lua=lua}end return {{CodeBlock=function(_NgKSEFg) if _NgKSEFg.classes:includes("eval")then local ydBwQ3=_NgKSEFg.text print("⚙️ ",ydBwQ3)local Jo7L=K(ydBwQ3) print("",Jo7L["is_passed"],"→",Jo7L["preview"])if _NgKSEFg.classes:includes("replace")then return pandoc.CodeBlock(Jo7L["out"],{code=ydBwQ3})end end end}}