PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/home/htlwork.com/www/dev/magento/vendor/wikimedia/less.php/lib/Less/
Upload File :
Current File : /home/htlwork.com/www/dev/magento/vendor/wikimedia/less.php/lib/Less/Parser.php
<?php

require_once dirname( __FILE__ ).'/Cache.php';

/**
 * Class for parsing and compiling less files into css
 *
 * @package Less
 * @subpackage parser
 *
 */
class Less_Parser {

	/**
	 * Default parser options
	 */
	public static $default_options = array(
		'compress'				=> false,			// option - whether to compress
		'strictUnits'			=> false,			// whether units need to evaluate correctly
		'strictMath'			=> false,			// whether math has to be within parenthesis
		'relativeUrls'			=> true,			// option - whether to adjust URL's to be relative
		'urlArgs'				=> '',				// whether to add args into url tokens
		'numPrecision'			=> 8,

		'import_dirs'			=> array(),
		'import_callback'		=> null,
		'cache_dir'				=> null,
		'cache_method'			=> 'php', 			// false, 'serialize', 'php', 'var_export', 'callback';
		'cache_callback_get'	=> null,
		'cache_callback_set'	=> null,

		'sourceMap'				=> false,			// whether to output a source map
		'sourceMapBasepath'		=> null,
		'sourceMapWriteTo'		=> null,
		'sourceMapURL'			=> null,

		'indentation' 			=> '  ',

		'plugins'				=> array(),

	);

	public static $options = array();

	private $input;					// Less input string
	private $input_len;				// input string length
	private $pos;					// current index in `input`
	private $saveStack = array();	// holds state for backtracking
	private $furthest;
	private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding

	/**
	 * @var Less_Environment
	 */
	private $env;

	protected $rules = array();

	private static $imports = array();

	public static $has_extends = false;

	public static $next_id = 0;

	/**
	 * Filename to contents of all parsed the files
	 *
	 * @var array
	 */
	public static $contentsMap = array();

	/**
	 * @param Less_Environment|array|null $env
	 */
	public function __construct( $env = null ) {
		// Top parser on an import tree must be sure there is one "env"
		// which will then be passed around by reference.
		if ( $env instanceof Less_Environment ) {
			$this->env = $env;
		} else {
			$this->SetOptions( Less_Parser::$default_options );
			$this->Reset( $env );
		}

		// mbstring.func_overload > 1 bugfix
		// The encoding value must be set for each source file,
		// therefore, to conserve resources and improve the speed of this design is taken here
		if ( ini_get( 'mbstring.func_overload' ) ) {
			$this->mb_internal_encoding = ini_get( 'mbstring.internal_encoding' );
			@ini_set( 'mbstring.internal_encoding', 'ascii' );
		}

	}

	/**
	 * Reset the parser state completely
	 *
	 */
	public function Reset( $options = null ) {
		$this->rules = array();
		self::$imports = array();
		self::$has_extends = false;
		self::$imports = array();
		self::$contentsMap = array();

		$this->env = new Less_Environment( $options );

		// set new options
		if ( is_array( $options ) ) {
			$this->SetOptions( Less_Parser::$default_options );
			$this->SetOptions( $options );
		}

		$this->env->Init();
	}

	/**
	 * Set one or more compiler options
	 *  options: import_dirs, cache_dir, cache_method
	 *
	 */
	public function SetOptions( $options ) {
		foreach ( $options as $option => $value ) {
			$this->SetOption( $option, $value );
		}
	}

	/**
	 * Set one compiler option
	 *
	 */
	public function SetOption( $option, $value ) {
		switch ( $option ) {

			case 'import_dirs':
				$this->SetImportDirs( $value );
			return;

			case 'cache_dir':
				if ( is_string( $value ) ) {
					Less_Cache::SetCacheDir( $value );
					Less_Cache::CheckCacheDir();
				}
			return;
		}

		Less_Parser::$options[$option] = $value;
	}

	/**
	 * Registers a new custom function
	 *
	 * @param string $name function name
	 * @param callable $callback callback
	 */
	public function registerFunction( $name, $callback ) {
		$this->env->functions[$name] = $callback;
	}

	/**
	 * Removed an already registered function
	 *
	 * @param string $name function name
	 */
	public function unregisterFunction( $name ) {
		if ( isset( $this->env->functions[$name] ) )
			unset( $this->env->functions[$name] );
	}

	/**
	 * Get the current css buffer
	 *
	 * @return string
	 */
	public function getCss() {
		$precision = ini_get( 'precision' );
		@ini_set( 'precision', 16 );
		$locale = setlocale( LC_NUMERIC, 0 );
		setlocale( LC_NUMERIC, "C" );

		try {

			$root = new Less_Tree_Ruleset( array(), $this->rules );
			$root->root = true;
			$root->firstRoot = true;

			$this->PreVisitors( $root );

			self::$has_extends = false;
			$evaldRoot = $root->compile( $this->env );

			$this->PostVisitors( $evaldRoot );

			if ( Less_Parser::$options['sourceMap'] ) {
				$generator = new Less_SourceMap_Generator( $evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
				// will also save file
				// FIXME: should happen somewhere else?
				$css = $generator->generateCSS();
			} else {
				$css = $evaldRoot->toCSS();
			}

			if ( Less_Parser::$options['compress'] ) {
				$css = preg_replace( '/(^(\s)+)|((\s)+$)/', '', $css );
			}

		} catch ( Exception $exc ) {
			// Intentional fall-through so we can reset environment
		}

		// reset php settings
		@ini_set( 'precision', $precision );
		setlocale( LC_NUMERIC, $locale );

		// If you previously defined $this->mb_internal_encoding
		// is required to return the encoding as it was before
		if ( $this->mb_internal_encoding != '' ) {
			@ini_set( "mbstring.internal_encoding", $this->mb_internal_encoding );
			$this->mb_internal_encoding = '';
		}

		// Rethrow exception after we handled resetting the environment
		if ( !empty( $exc ) ) {
			throw $exc;
		}

		return $css;
	}

	public function findValueOf( $varName ) {
		foreach ( $this->rules as $rule ) {
			if ( isset( $rule->variable ) && ( $rule->variable == true ) && ( str_replace( "@", "", $rule->name ) == $varName ) ) {
				return $this->getVariableValue( $rule );
			}
		}
		return null;
	}

	/**
	 *
	 * this function gets the private rules variable and returns an array of the found variables
	 * it uses a helper method getVariableValue() that contains the logic ot fetch the value from the rule object
	 *
	 * @return array
	 */
	public function getVariables() {
		$variables = array();

		$not_variable_type = array(
			'Comment',   // this include less comments ( // ) and css comments (/* */)
			'Import',    // do not search variables in included files @import
			'Ruleset',   // selectors (.someclass, #someid, …)
			'Operation', //
		);

		// @TODO run compilation if not runned yet
		foreach ( $this->rules as $key => $rule ) {
			if ( in_array( $rule->type, $not_variable_type ) ) {
				continue;
			}

			// Note: it seems rule->type is always Rule when variable = true
			if ( $rule->type == 'Rule' && $rule->variable ) {
				$variables[$rule->name] = $this->getVariableValue( $rule );
			} else {
				if ( $rule->type == 'Comment' ) {
					$variables[] = $this->getVariableValue( $rule );
				}
			}
		}
		return $variables;
	}

	public function findVarByName( $var_name ) {
		foreach ( $this->rules as $rule ) {
			if ( isset( $rule->variable ) && ( $rule->variable == true ) ) {
				if ( $rule->name == $var_name ) {
					return $this->getVariableValue( $rule );
				}
			}
		}
		return null;
	}

	/**
	 *
	 * This method gets the value of the less variable from the rules object.
	 * Since the objects vary here we add the logic for extracting the css/less value.
	 *
	 * @param $var
	 *
	 * @return bool|string
	 */
	private function getVariableValue( $var ) {
		if ( !is_a( $var, 'Less_Tree' ) ) {
			throw new Exception( 'var is not a Less_Tree object' );
		}

		switch ( $var->type ) {
			case 'Color':
				return $this->rgb2html( $var->rgb );
			case 'Unit':
				return $var->value. $var->unit->numerator[0];
			case 'Variable':
				return $this->findVarByName( $var->name );
			case 'Keyword':
				return $var->value;
			case 'Rule':
				return $this->getVariableValue( $var->value );
			case 'Value':
				$value = '';
				foreach ( $var->value as $sub_value ) {
					$value .= $this->getVariableValue( $sub_value ).' ';
				}
				return $value;
			case 'Quoted':
				return $var->quote.$var->value.$var->quote;
			case 'Dimension':
				$value = $var->value;
				if ( $var->unit && $var->unit->numerator ) {
					$value .= $var->unit->numerator[0];
				}
				return $value;
			case 'Expression':
				$value = "";
				foreach ( $var->value as $item ) {
					$value .= $this->getVariableValue( $item )." ";
				}
				return $value;
			case 'Operation':
				throw new Exception( 'getVariables() require Less to be compiled. please use $parser->getCss() before calling getVariables()' );
			case 'Comment':
			case 'Import':
			case 'Ruleset':
			default:
				throw new Exception( "type missing in switch/case getVariableValue for ".$var->type );
		}
		return false;
	}

	private function rgb2html( $r, $g = -1, $b = -1 ) {
		if ( is_array( $r ) && sizeof( $r ) == 3 )
			list( $r, $g, $b ) = $r;

		$r = intval( $r ); $g = intval( $g );
		$b = intval( $b );

		$r = dechex( $r < 0 ? 0 : ( $r > 255 ? 255 : $r ) );
		$g = dechex( $g < 0 ? 0 : ( $g > 255 ? 255 : $g ) );
		$b = dechex( $b < 0 ? 0 : ( $b > 255 ? 255 : $b ) );

		$color = ( strlen( $r ) < 2 ? '0' : '' ).$r;
		$color .= ( strlen( $g ) < 2 ? '0' : '' ).$g;
		$color .= ( strlen( $b ) < 2 ? '0' : '' ).$b;
		return '#'.$color;
	}

	/**
	 * Run pre-compile visitors
	 *
	 */
	private function PreVisitors( $root ) {
		if ( Less_Parser::$options['plugins'] ) {
			foreach ( Less_Parser::$options['plugins'] as $plugin ) {
				if ( !empty( $plugin->isPreEvalVisitor ) ) {
					$plugin->run( $root );
				}
			}
		}
	}

	/**
	 * Run post-compile visitors
	 *
	 */
	private function PostVisitors( $evaldRoot ) {
		$visitors = array();
		$visitors[] = new Less_Visitor_joinSelector();
		if ( self::$has_extends ) {
			$visitors[] = new Less_Visitor_processExtends();
		}
		$visitors[] = new Less_Visitor_toCSS();

		if ( Less_Parser::$options['plugins'] ) {
			foreach ( Less_Parser::$options['plugins'] as $plugin ) {
				if ( property_exists( $plugin, 'isPreEvalVisitor' ) && $plugin->isPreEvalVisitor ) {
					continue;
				}

				if ( property_exists( $plugin, 'isPreVisitor' ) && $plugin->isPreVisitor ) {
					array_unshift( $visitors, $plugin );
				} else {
					$visitors[] = $plugin;
				}
			}
		}

		for ( $i = 0; $i < count( $visitors ); $i++ ) {
			$visitors[$i]->run( $evaldRoot );
		}

	}

	/**
	 * Parse a Less string into css
	 *
	 * @param string $str The string to convert
	 * @param string $uri_root The url of the file
	 * @return Less_Tree_Ruleset|Less_Parser
	 */
	public function parse( $str, $file_uri = null ) {
		if ( !$file_uri ) {
			$uri_root = '';
			$filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
		} else {
			$file_uri = self::WinPath( $file_uri );
			$filename = $file_uri;
			$uri_root = dirname( $file_uri );
		}

		$previousFileInfo = $this->env->currentFileInfo;
		$uri_root = self::WinPath( $uri_root );
		$this->SetFileInfo( $filename, $uri_root );

		$this->input = $str;
		$this->_parse();

		if ( $previousFileInfo ) {
			$this->env->currentFileInfo = $previousFileInfo;
		}

		return $this;
	}

	/**
	 * Parse a Less string from a given file
	 *
	 * @throws Less_Exception_Parser
	 * @param string $filename The file to parse
	 * @param string $uri_root The url of the file
	 * @param bool $returnRoot Indicates whether the return value should be a css string a root node
	 * @return Less_Tree_Ruleset|Less_Parser
	 */
	public function parseFile( $filename, $uri_root = '', $returnRoot = false ) {
		if ( !file_exists( $filename ) ) {
			$this->Error( sprintf( 'File `%s` not found.', $filename ) );
		}

		// fix uri_root?
		// Instead of The mixture of file path for the first argument and directory path for the second argument has bee
		if ( !$returnRoot && !empty( $uri_root ) && basename( $uri_root ) == basename( $filename ) ) {
			$uri_root = dirname( $uri_root );
		}

		$previousFileInfo = $this->env->currentFileInfo;

		if ( $filename ) {
			$filename = self::AbsPath( $filename, true );
		}
		$uri_root = self::WinPath( $uri_root );

		$this->SetFileInfo( $filename, $uri_root );

		self::AddParsedFile( $filename );

		if ( $returnRoot ) {
			$rules = $this->GetRules( $filename );
			$return = new Less_Tree_Ruleset( array(), $rules );
		} else {
			$this->_parse( $filename );
			$return = $this;
		}

		if ( $previousFileInfo ) {
			$this->env->currentFileInfo = $previousFileInfo;
		}

		return $return;
	}

	/**
	 * Allows a user to set variables values
	 * @param array $vars
	 * @return Less_Parser
	 */
	public function ModifyVars( $vars ) {
		$this->input = Less_Parser::serializeVars( $vars );
		$this->_parse();

		return $this;
	}

	/**
	 * @param string $filename
	 */
	public function SetFileInfo( $filename, $uri_root = '' ) {
		$filename = Less_Environment::normalizePath( $filename );
		$dirname = preg_replace( '/[^\/\\\\]*$/', '', $filename );

		if ( !empty( $uri_root ) ) {
			$uri_root = rtrim( $uri_root, '/' ).'/';
		}

		$currentFileInfo = array();

		// entry info
		if ( isset( $this->env->currentFileInfo ) ) {
			$currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
			$currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
			$currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];

		} else {
			$currentFileInfo['entryPath'] = $dirname;
			$currentFileInfo['entryUri'] = $uri_root;
			$currentFileInfo['rootpath'] = $dirname;
		}

		$currentFileInfo['currentDirectory'] = $dirname;
		$currentFileInfo['currentUri'] = $uri_root.basename( $filename );
		$currentFileInfo['filename'] = $filename;
		$currentFileInfo['uri_root'] = $uri_root;

		// inherit reference
		if ( isset( $this->env->currentFileInfo['reference'] ) && $this->env->currentFileInfo['reference'] ) {
			$currentFileInfo['reference'] = true;
		}

		$this->env->currentFileInfo = $currentFileInfo;
	}

	/**
	 * @deprecated 1.5.1.2
	 *
	 */
	public function SetCacheDir( $dir ) {
		if ( !file_exists( $dir ) ) {
			if ( mkdir( $dir ) ) {
				return true;
			}
			throw new Less_Exception_Parser( 'Less.php cache directory couldn\'t be created: '.$dir );

		} elseif ( !is_dir( $dir ) ) {
			throw new Less_Exception_Parser( 'Less.php cache directory doesn\'t exist: '.$dir );

		} elseif ( !is_writable( $dir ) ) {
			throw new Less_Exception_Parser( 'Less.php cache directory isn\'t writable: '.$dir );

		} else {
			$dir = self::WinPath( $dir );
			Less_Cache::$cache_dir = rtrim( $dir, '/' ).'/';
			return true;
		}
	}

	/**
	 * Set a list of directories or callbacks the parser should use for determining import paths
	 *
	 * @param array $dirs
	 */
	public function SetImportDirs( $dirs ) {
		Less_Parser::$options['import_dirs'] = array();

		foreach ( $dirs as $path => $uri_root ) {

			$path = self::WinPath( $path );
			if ( !empty( $path ) ) {
				$path = rtrim( $path, '/' ).'/';
			}

			if ( !is_callable( $uri_root ) ) {
				$uri_root = self::WinPath( $uri_root );
				if ( !empty( $uri_root ) ) {
					$uri_root = rtrim( $uri_root, '/' ).'/';
				}
			}

			Less_Parser::$options['import_dirs'][$path] = $uri_root;
		}
	}

	/**
	 * @param string $file_path
	 */
	private function _parse( $file_path = null ) {
		$this->rules = array_merge( $this->rules, $this->GetRules( $file_path ) );
	}

	/**
	 * Return the results of parsePrimary for $file_path
	 * Use cache and save cached results if possible
	 *
	 * @param string|null $file_path
	 */
	private function GetRules( $file_path ) {
		$this->SetInput( $file_path );

		$cache_file = $this->CacheFile( $file_path );
		if ( $cache_file ) {
			if ( Less_Parser::$options['cache_method'] == 'callback' ) {
				if ( is_callable( Less_Parser::$options['cache_callback_get'] ) ) {
					$cache = call_user_func_array(
						Less_Parser::$options['cache_callback_get'],
						array( $this, $file_path, $cache_file )
					);

					if ( $cache ) {
						$this->UnsetInput();
						return $cache;
					}
				}

			} elseif ( file_exists( $cache_file ) ) {
				switch ( Less_Parser::$options['cache_method'] ) {

					// Using serialize
					// Faster but uses more memory
					case 'serialize':
						$cache = unserialize( file_get_contents( $cache_file ) );
						if ( $cache ) {
							touch( $cache_file );
							$this->UnsetInput();
							return $cache;
						}
						break;

						// Using generated php code
					case 'var_export':
					case 'php':
						$this->UnsetInput();
						return include $cache_file;
				}
			}
		}

		$rules = $this->parsePrimary();

		if ( $this->pos < $this->input_len ) {
			throw new Less_Exception_Chunk( $this->input, null, $this->furthest, $this->env->currentFileInfo );
		}

		$this->UnsetInput();

		// save the cache
		if ( $cache_file ) {
			if ( Less_Parser::$options['cache_method'] == 'callback' ) {
				if ( is_callable( Less_Parser::$options['cache_callback_set'] ) ) {
					call_user_func_array(
						Less_Parser::$options['cache_callback_set'],
						array( $this, $file_path, $cache_file, $rules )
					);
				}

			} else {
				// msg('write cache file');
				switch ( Less_Parser::$options['cache_method'] ) {
					case 'serialize':
						file_put_contents( $cache_file, serialize( $rules ) );
						break;
					case 'php':
						file_put_contents( $cache_file, '<?php return '.self::ArgString( $rules ).'; ?>' );
						break;
					case 'var_export':
						// Requires __set_state()
						file_put_contents( $cache_file, '<?php return '.var_export( $rules, true ).'; ?>' );
						break;
				}

				Less_Cache::CleanCache();
			}
		}

		return $rules;
	}

	/**
	 * Set up the input buffer
	 *
	 */
	public function SetInput( $file_path ) {
		if ( $file_path ) {
			$this->input = file_get_contents( $file_path );
		}

		$this->pos = $this->furthest = 0;

		// Remove potential UTF Byte Order Mark
		$this->input = preg_replace( '/\\G\xEF\xBB\xBF/', '', $this->input );
		$this->input_len = strlen( $this->input );

		if ( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ) {
			$uri = $this->env->currentFileInfo['currentUri'];
			Less_Parser::$contentsMap[$uri] = $this->input;
		}

	}

	/**
	 * Free up some memory
	 *
	 */
	public function UnsetInput() {
		unset( $this->input, $this->pos, $this->input_len, $this->furthest );
		$this->saveStack = array();
	}

	public function CacheFile( $file_path ) {
		if ( $file_path && $this->CacheEnabled() ) {

			$env = get_object_vars( $this->env );
			unset( $env['frames'] );

			$parts = array();
			$parts[] = $file_path;
			$parts[] = filesize( $file_path );
			$parts[] = filemtime( $file_path );
			$parts[] = $env;
			$parts[] = Less_Version::cache_version;
			$parts[] = Less_Parser::$options['cache_method'];
			return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1( json_encode( $parts ) ), 16, 36 ) . '.lesscache';
		}
	}

	static function AddParsedFile( $file ) {
		self::$imports[] = $file;
	}

	static function AllParsedFiles() {
		return self::$imports;
	}

	/**
	 * @param string $file
	 */
	static function FileParsed( $file ) {
		return in_array( $file, self::$imports );
	}

	function save() {
		$this->saveStack[] = $this->pos;
	}

	private function restore() {
		$this->pos = array_pop( $this->saveStack );
	}

	private function forget() {
		array_pop( $this->saveStack );
	}

	/**
	 * Determine if the character at the specified offset from the current position is a white space.
	 *
	 * @param int $offset
	 *
	 * @return bool
	 */
	private function isWhitespace( $offset = 0 ) {
		return strpos( " \t\n\r\v\f", $this->input[$this->pos + $offset] ) !== false;
	}

	/**
	 * Parse from a token, regexp or string, and move forward if match
	 *
	 * @param array $toks
	 * @return array
	 */
	private function match( $toks ) {
		// The match is confirmed, add the match length to `this::pos`,
		// and consume any extra white-space characters (' ' || '\n')
		// which come after that. The reason for this is that LeSS's
		// grammar is mostly white-space insensitive.
		//

		foreach ( $toks as $tok ) {

			$char = $tok[0];

			if ( $char === '/' ) {
				$match = $this->MatchReg( $tok );

				if ( $match ) {
					return count( $match ) === 1 ? $match[0] : $match;
				}

			} elseif ( $char === '#' ) {
				$match = $this->MatchChar( $tok[1] );

			} else {
				// Non-terminal, match using a function call
				$match = $this->$tok();

			}

			if ( $match ) {
				return $match;
			}
		}
	}

	/**
	 * @param string[] $toks
	 *
	 * @return string
	 */
	private function MatchFuncs( $toks ) {
		if ( $this->pos < $this->input_len ) {
			foreach ( $toks as $tok ) {
				$match = $this->$tok();
				if ( $match ) {
					return $match;
				}
			}
		}

	}

	// Match a single character in the input,
	private function MatchChar( $tok ) {
		if ( ( $this->pos < $this->input_len ) && ( $this->input[$this->pos] === $tok ) ) {
			$this->skipWhitespace( 1 );
			return $tok;
		}
	}

	// Match a regexp from the current start point
	private function MatchReg( $tok ) {
		if ( preg_match( $tok, $this->input, $match, 0, $this->pos ) ) {
			$this->skipWhitespace( strlen( $match[0] ) );
			return $match;
		}
	}

	/**
	 * Same as match(), but don't change the state of the parser,
	 * just return the match.
	 *
	 * @param string $tok
	 * @return integer
	 */
	public function PeekReg( $tok ) {
		return preg_match( $tok, $this->input, $match, 0, $this->pos );
	}

	/**
	 * @param string $tok
	 */
	public function PeekChar( $tok ) {
		// return ($this->input[$this->pos] === $tok );
		return ( $this->pos < $this->input_len ) && ( $this->input[$this->pos] === $tok );
	}

	/**
	 * @param integer $length
	 */
	public function skipWhitespace( $length ) {
		$this->pos += $length;

		for ( ; $this->pos < $this->input_len; $this->pos++ ) {
			$c = $this->input[$this->pos];

			if ( ( $c !== "\n" ) && ( $c !== "\r" ) && ( $c !== "\t" ) && ( $c !== ' ' ) ) {
				break;
			}
		}
	}

	/**
	 * @param string $tok
	 * @param string|null $msg
	 */
	public function expect( $tok, $msg = NULL ) {
		$result = $this->match( array( $tok ) );
		if ( !$result ) {
			$this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
		} else {
			return $result;
		}
	}

	/**
	 * @param string $tok
	 */
	public function expectChar( $tok, $msg = null ) {
		$result = $this->MatchChar( $tok );
		if ( !$result ) {
			$msg = $msg ? $msg : "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'";
			$this->Error( $msg );
		} else {
			return $result;
		}
	}

	//
	// Here in, the parsing rules/functions
	//
	// The basic structure of the syntax tree generated is as follows:
	//
	//   Ruleset ->  Rule -> Value -> Expression -> Entity
	//
	// Here's some LESS code:
	//
	//	.class {
	//	  color: #fff;
	//	  border: 1px solid #000;
	//	  width: @w + 4px;
	//	  > .child {...}
	//	}
	//
	// And here's what the parse tree might look like:
	//
	//	 Ruleset (Selector '.class', [
	//		 Rule ("color",  Value ([Expression [Color #fff]]))
	//		 Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
	//		 Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
	//		 Ruleset (Selector [Element '>', '.child'], [...])
	//	 ])
	//
	//  In general, most rules will try to parse a token with the `$()` function, and if the return
	//  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
	//  first, before parsing, that's when we use `peek()`.
	//

	//
	// The `primary` rule is the *entry* and *exit* point of the parser.
	// The rules here can appear at any level of the parse tree.
	//
	// The recursive nature of the grammar is an interplay between the `block`
	// rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
	// as represented by this simplified grammar:
	//
	//	 primary  →  (ruleset | rule)+
	//	 ruleset  →  selector+ block
	//	 block	→  '{' primary '}'
	//
	// Only at one point is the primary rule not called from the
	// block rule: at the root level.
	//
	private function parsePrimary() {
		$root = array();

		while ( true ) {

			if ( $this->pos >= $this->input_len ) {
				break;
			}

			$node = $this->parseExtend( true );
			if ( $node ) {
				$root = array_merge( $root, $node );
				continue;
			}

			// $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
			$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective' ) );

			if ( $node ) {
				$root[] = $node;
			} elseif ( !$this->MatchReg( '/\\G[\s\n;]+/' ) ) {
				break;
			}

			if ( $this->PeekChar( '}' ) ) {
				break;
			}
		}

		return $root;
	}

	// We create a Comment node for CSS comments `/* */`,
	// but keep the LeSS comments `//` silent, by just skipping
	// over them.
	private function parseComment() {
		if ( $this->input[$this->pos] !== '/' ) {
			return;
		}

		if ( $this->input[$this->pos + 1] === '/' ) {
			$match = $this->MatchReg( '/\\G\/\/.*/' );
			return $this->NewObj4( 'Less_Tree_Comment', array( $match[0], true, $this->pos, $this->env->currentFileInfo ) );
		}

		// $comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
		$comment = $this->MatchReg( '/\\G\/\*(?s).*?\*+\/\n?/' );// not the same as less.js to prevent fatal errors
		if ( $comment ) {
			return $this->NewObj4( 'Less_Tree_Comment', array( $comment[0], false, $this->pos, $this->env->currentFileInfo ) );
		}
	}

	private function parseComments() {
		$comments = array();

		while ( $this->pos < $this->input_len ) {
			$comment = $this->parseComment();
			if ( !$comment ) {
				break;
			}

			$comments[] = $comment;
		}

		return $comments;
	}

	//
	// A string, which supports escaping " and '
	//
	//	 "milky way" 'he\'s the one!'
	//
	private function parseEntitiesQuoted() {
		$j = $this->pos;
		$e = false;
		$index = $this->pos;

		if ( $this->input[$this->pos] === '~' ) {
			$j++;
			$e = true; // Escaped strings
		}

		$char = $this->input[$j];
		if ( $char !== '"' && $char !== "'" ) {
			return;
		}

		if ( $e ) {
			$this->MatchChar( '~' );
		}

		$matched = $this->MatchQuoted( $char, $j + 1 );
		if ( $matched === false ) {
			return;
		}

		$quoted = $char.$matched.$char;
		return $this->NewObj5( 'Less_Tree_Quoted', array( $quoted, $matched, $e, $index, $this->env->currentFileInfo ) );
	}

	/**
	 * When PCRE JIT is enabled in php, regular expressions don't work for matching quoted strings
	 *
	 *	$regex	= '/\\G\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/';
	 *	$regex	= '/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"/';
	 *
	 */
	private function MatchQuoted( $quote_char, $i ) {
		$matched = '';
		while ( $i < $this->input_len ) {
			$c = $this->input[$i];

			// escaped character
			if ( $c === '\\' ) {
				$matched .= $c . $this->input[$i + 1];
				$i += 2;
				continue;
			}

			if ( $c === $quote_char ) {
				$this->pos = $i + 1;
				$this->skipWhitespace( 0 );
				return $matched;
			}

			if ( $c === "\r" || $c === "\n" ) {
				return false;
			}

			$i++;
			$matched .= $c;
		}

		return false;
	}

	//
	// A catch-all word, such as:
	//
	//	 black border-collapse
	//
	private function parseEntitiesKeyword() {
		// $k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
		$k = $this->MatchReg( '/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/' );
		if ( $k ) {
			$k = $k[0];
			$color = $this->fromKeyword( $k );
			if ( $color ) {
				return $color;
			}
			return $this->NewObj1( 'Less_Tree_Keyword', $k );
		}
	}

	// duplicate of Less_Tree_Color::FromKeyword
	private function FromKeyword( $keyword ) {
		$keyword = strtolower( $keyword );

		if ( Less_Colors::hasOwnProperty( $keyword ) ) {
			// detect named color
			return $this->NewObj1( 'Less_Tree_Color', substr( Less_Colors::color( $keyword ), 1 ) );
		}

		if ( $keyword === 'transparent' ) {
			return $this->NewObj3( 'Less_Tree_Color', array( array( 0, 0, 0 ), 0, true ) );
		}
	}

	//
	// A function call
	//
	//	 rgb(255, 0, 255)
	//
	// We also try to catch IE's `alpha()`, but let the `alpha` parser
	// deal with the details.
	//
	// The arguments are parsed with the `entities.arguments` parser.
	//
	private function parseEntitiesCall() {
		$index = $this->pos;

		if ( !preg_match( '/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name, 0, $this->pos ) ) {
			return;
		}
		$name = $name[1];
		$nameLC = strtolower( $name );

		if ( $nameLC === 'url' ) {
			return null;
		}

		$this->pos += strlen( $name );

		if ( $nameLC === 'alpha' ) {
			$alpha_ret = $this->parseAlpha();
			if ( $alpha_ret ) {
				return $alpha_ret;
			}
		}

		$this->MatchChar( '(' ); // Parse the '(' and consume whitespace.

		$args = $this->parseEntitiesArguments();

		if ( !$this->MatchChar( ')' ) ) {
			return;
		}

		if ( $name ) {
			return $this->NewObj4( 'Less_Tree_Call', array( $name, $args, $index, $this->env->currentFileInfo ) );
		}
	}

	/**
	 * Parse a list of arguments
	 *
	 * @return array
	 */
	private function parseEntitiesArguments() {
		$args = array();
		while ( true ) {
			$arg = $this->MatchFuncs( array( 'parseEntitiesAssignment','parseExpression' ) );
			if ( !$arg ) {
				break;
			}

			$args[] = $arg;
			if ( !$this->MatchChar( ',' ) ) {
				break;
			}
		}
		return $args;
	}

	private function parseEntitiesLiteral() {
		return $this->MatchFuncs( array( 'parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor' ) );
	}

	// Assignments are argument entities for calls.
	// They are present in ie filter properties as shown below.
	//
	//	 filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
	//
	private function parseEntitiesAssignment() {
		$key = $this->MatchReg( '/\\G\w+(?=\s?=)/' );
		if ( !$key ) {
			return;
		}

		if ( !$this->MatchChar( '=' ) ) {
			return;
		}

		$value = $this->parseEntity();
		if ( $value ) {
			return $this->NewObj2( 'Less_Tree_Assignment', array( $key[0], $value ) );
		}
	}

	//
	// Parse url() tokens
	//
	// We use a specific rule for urls, because they don't really behave like
	// standard function calls. The difference is that the argument doesn't have
	// to be enclosed within a string, so it can't be parsed as an Expression.
	//
	private function parseEntitiesUrl() {
		if ( $this->input[$this->pos] !== 'u' || !$this->matchReg( '/\\Gurl\(/' ) ) {
			return;
		}

		$value = $this->match( array( 'parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/' ) );
		if ( !$value ) {
			$value = '';
		}

		$this->expectChar( ')' );

		if ( isset( $value->value ) || $value instanceof Less_Tree_Variable ) {
			return $this->NewObj2( 'Less_Tree_Url', array( $value, $this->env->currentFileInfo ) );
		}

		return $this->NewObj2( 'Less_Tree_Url', array( $this->NewObj1( 'Less_Tree_Anonymous', $value ), $this->env->currentFileInfo ) );
	}

	//
	// A Variable entity, such as `@fink`, in
	//
	//	 width: @fink + 2px
	//
	// We use a different parser for variable definitions,
	// see `parsers.variable`.
	//
	private function parseEntitiesVariable() {
		$index = $this->pos;
		if ( $this->PeekChar( '@' ) && ( $name = $this->MatchReg( '/\\G@@?[\w-]+/' ) ) ) {
			return $this->NewObj3( 'Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo ) );
		}
	}

	// A variable entity using the protective {} e.g. @{var}
	private function parseEntitiesVariableCurly() {
		$index = $this->pos;

		if ( $this->input_len > ( $this->pos + 1 ) && $this->input[$this->pos] === '@' && ( $curly = $this->MatchReg( '/\\G@\{([\w-]+)\}/' ) ) ) {
			return $this->NewObj3( 'Less_Tree_Variable', array( '@'.$curly[1], $index, $this->env->currentFileInfo ) );
		}
	}

	//
	// A Hexadecimal color
	//
	//	 #4F3C2F
	//
	// `rgb` and `hsl` colors are parsed through the `entities.call` parser.
	//
	private function parseEntitiesColor() {
		if ( $this->PeekChar( '#' ) && ( $rgb = $this->MatchReg( '/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/' ) ) ) {
			return $this->NewObj1( 'Less_Tree_Color', $rgb[1] );
		}
	}

	//
	// A Dimension, that is, a number and a unit
	//
	//	 0.5em 95%
	//
	private function parseEntitiesDimension() {
		$c = @ord( $this->input[$this->pos] );

		// Is the first char of the dimension 0-9, '.', '+' or '-'
		if ( ( $c > 57 || $c < 43 ) || $c === 47 || $c == 44 ) {
			return;
		}

		$value = $this->MatchReg( '/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/' );
		if ( $value ) {

			if ( isset( $value[2] ) ) {
				return $this->NewObj2( 'Less_Tree_Dimension', array( $value[1],$value[2] ) );
			}
			return $this->NewObj1( 'Less_Tree_Dimension', $value[1] );
		}
	}

	//
	// A unicode descriptor, as is used in unicode-range
	//
	// U+0?? or U+00A1-00A9
	//
	function parseUnicodeDescriptor() {
		$ud = $this->MatchReg( '/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/' );
		if ( $ud ) {
			return $this->NewObj1( 'Less_Tree_UnicodeDescriptor', $ud[0] );
		}
	}

	//
	// JavaScript code to be evaluated
	//
	//	 `window.location.href`
	//
	private function parseEntitiesJavascript() {
		$e = false;
		$j = $this->pos;
		if ( $this->input[$j] === '~' ) {
			$j++;
			$e = true;
		}
		if ( $this->input[$j] !== '`' ) {
			return;
		}
		if ( $e ) {
			$this->MatchChar( '~' );
		}
		$str = $this->MatchReg( '/\\G`([^`]*)`/' );
		if ( $str ) {
			return $this->NewObj3( 'Less_Tree_Javascript', array( $str[1], $this->pos, $e ) );
		}
	}

	//
	// The variable part of a variable definition. Used in the `rule` parser
	//
	//	 @fink:
	//
	private function parseVariable() {
		if ( $this->PeekChar( '@' ) && ( $name = $this->MatchReg( '/\\G(@[\w-]+)\s*:/' ) ) ) {
			return $name[1];
		}
	}

	//
	// The variable part of a variable definition. Used in the `rule` parser
	//
	// @fink();
	//
	private function parseRulesetCall() {
		if ( $this->input[$this->pos] === '@' && ( $name = $this->MatchReg( '/\\G(@[\w-]+)\s*\(\s*\)\s*;/' ) ) ) {
			return $this->NewObj1( 'Less_Tree_RulesetCall', $name[1] );
		}
	}

	//
	// extend syntax - used to extend selectors
	//
	function parseExtend( $isRule = false ) {
		$index = $this->pos;
		$extendList = array();

		if ( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ) { return;
		}

		do{
			$option = null;
			$elements = array();
			while ( true ) {
				$option = $this->MatchReg( '/\\G(all)(?=\s*(\)|,))/' );
				if ( $option ) { break;
				}
				$e = $this->parseElement();
				if ( !$e ) { break;
				}
				$elements[] = $e;
			}

			if ( $option ) {
				$option = $option[1];
			}

			$extendList[] = $this->NewObj3( 'Less_Tree_Extend', array( $this->NewObj1( 'Less_Tree_Selector', $elements ), $option, $index ) );

		}while ( $this->MatchChar( "," ) );

		$this->expect( '/\\G\)/' );

		if ( $isRule ) {
			$this->expect( '/\\G;/' );
		}

		return $extendList;
	}

	//
	// A Mixin call, with an optional argument list
	//
	//	 #mixins > .square(#fff);
	//	 .rounded(4px, black);
	//	 .button;
	//
	// The `while` loop is there because mixins can be
	// namespaced, but we only support the child and descendant
	// selector for now.
	//
	private function parseMixinCall() {
		$char = $this->input[$this->pos];
		if ( $char !== '.' && $char !== '#' ) {
			return;
		}

		$index = $this->pos;
		$this->save(); // stop us absorbing part of an invalid selector

		$elements = $this->parseMixinCallElements();

		if ( $elements ) {

			if ( $this->MatchChar( '(' ) ) {
				$returned = $this->parseMixinArgs( true );
				$args = $returned['args'];
				$this->expectChar( ')' );
			} else {
				$args = array();
			}

			$important = $this->parseImportant();

			if ( $this->parseEnd() ) {
				$this->forget();
				return $this->NewObj5( 'Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important ) );
			}
		}

		$this->restore();
	}

	private function parseMixinCallElements() {
		$elements = array();
		$c = null;

		while ( true ) {
			$elemIndex = $this->pos;
			$e = $this->MatchReg( '/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/' );
			if ( !$e ) {
				break;
			}
			$elements[] = $this->NewObj4( 'Less_Tree_Element', array( $c, $e[0], $elemIndex, $this->env->currentFileInfo ) );
			$c = $this->MatchChar( '>' );
		}

		return $elements;
	}

	/**
	 * @param boolean $isCall
	 */
	private function parseMixinArgs( $isCall ) {
		$expressions = array();
		$argsSemiColon = array();
		$isSemiColonSeperated = null;
		$argsComma = array();
		$expressionContainsNamed = null;
		$name = null;
		$returner = array( 'args' => array(), 'variadic' => false );

		$this->save();

		while ( true ) {
			if ( $isCall ) {
				$arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
			} else {
				$this->parseComments();
				if ( $this->input[ $this->pos ] === '.' && $this->MatchReg( '/\\G\.{3}/' ) ) {
					$returner['variadic'] = true;
					if ( $this->MatchChar( ";" ) && !$isSemiColonSeperated ) {
						$isSemiColonSeperated = true;
					}

					if ( $isSemiColonSeperated ) {
						$argsSemiColon[] = array( 'variadic' => true );
					} else {
						$argsComma[] = array( 'variadic' => true );
					}
					break;
				}
				$arg = $this->MatchFuncs( array( 'parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword' ) );
			}

			if ( !$arg ) {
				break;
			}

			$nameLoop = null;
			if ( $arg instanceof Less_Tree_Expression ) {
				$arg->throwAwayComments();
			}
			$value = $arg;
			$val = null;

			if ( $isCall ) {
				// Variable
				if ( property_exists( $arg, 'value' ) && count( $arg->value ) == 1 ) {
					$val = $arg->value[0];
				}
			} else {
				$val = $arg;
			}

			if ( $val instanceof Less_Tree_Variable ) {

				if ( $this->MatchChar( ':' ) ) {
					if ( $expressions ) {
						if ( $isSemiColonSeperated ) {
							$this->Error( 'Cannot mix ; and , as delimiter types' );
						}
						$expressionContainsNamed = true;
					}

					// we do not support setting a ruleset as a default variable - it doesn't make sense
					// However if we do want to add it, there is nothing blocking it, just don't error
					// and remove isCall dependency below
					$value = null;
					if ( $isCall ) {
						$value = $this->parseDetachedRuleset();
					}
					if ( !$value ) {
						$value = $this->parseExpression();
					}

					if ( !$value ) {
						if ( $isCall ) {
							$this->Error( 'could not understand value for named argument' );
						} else {
							$this->restore();
							$returner['args'] = array();
							return $returner;
						}
					}

					$nameLoop = ( $name = $val->name );
				} elseif ( !$isCall && $this->MatchReg( '/\\G\.{3}/' ) ) {
					$returner['variadic'] = true;
					if ( $this->MatchChar( ";" ) && !$isSemiColonSeperated ) {
						$isSemiColonSeperated = true;
					}
					if ( $isSemiColonSeperated ) {
						$argsSemiColon[] = array( 'name' => $arg->name, 'variadic' => true );
					} else {
						$argsComma[] = array( 'name' => $arg->name, 'variadic' => true );
					}
					break;
				} elseif ( !$isCall ) {
					$name = $nameLoop = $val->name;
					$value = null;
				}
			}

			if ( $value ) {
				$expressions[] = $value;
			}

			$argsComma[] = array( 'name' => $nameLoop, 'value' => $value );

			if ( $this->MatchChar( ',' ) ) {
				continue;
			}

			if ( $this->MatchChar( ';' ) || $isSemiColonSeperated ) {

				if ( $expressionContainsNamed ) {
					$this->Error( 'Cannot mix ; and , as delimiter types' );
				}

				$isSemiColonSeperated = true;

				if ( count( $expressions ) > 1 ) {
					$value = $this->NewObj1( 'Less_Tree_Value', $expressions );
				}
				$argsSemiColon[] = array( 'name' => $name, 'value' => $value );

				$name = null;
				$expressions = array();
				$expressionContainsNamed = false;
			}
		}

		$this->forget();
		$returner['args'] = ( $isSemiColonSeperated ? $argsSemiColon : $argsComma );
		return $returner;
	}

	//
	// A Mixin definition, with a list of parameters
	//
	//	 .rounded (@radius: 2px, @color) {
	//		...
	//	 }
	//
	// Until we have a finer grained state-machine, we have to
	// do a look-ahead, to make sure we don't have a mixin call.
	// See the `rule` function for more information.
	//
	// We start by matching `.rounded (`, and then proceed on to
	// the argument list, which has optional default values.
	// We store the parameters in `params`, with a `value` key,
	// if there is a value, such as in the case of `@radius`.
	//
	// Once we've got our params list, and a closing `)`, we parse
	// the `{...}` block.
	//
	private function parseMixinDefinition() {
		$cond = null;

		$char = $this->input[$this->pos];
		if ( ( $char !== '.' && $char !== '#' ) || ( $char === '{' && $this->PeekReg( '/\\G[^{]*\}/' ) ) ) {
			return;
		}

		$this->save();

		$match = $this->MatchReg( '/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/' );
		if ( $match ) {
			$name = $match[1];

			$argInfo = $this->parseMixinArgs( false );
			$params = $argInfo['args'];
			$variadic = $argInfo['variadic'];

			// .mixincall("@{a}");
			// looks a bit like a mixin definition..
			// also
			// .mixincall(@a: {rule: set;});
			// so we have to be nice and restore
			if ( !$this->MatchChar( ')' ) ) {
				$this->furthest = $this->pos;
				$this->restore();
				return;
			}

			$this->parseComments();

			if ( $this->MatchReg( '/\\Gwhen/' ) ) { // Guard
				$cond = $this->expect( 'parseConditions', 'Expected conditions' );
			}

			$ruleset = $this->parseBlock();

			if ( is_array( $ruleset ) ) {
				$this->forget();
				return $this->NewObj5( 'Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic ) );
			}

			$this->restore();
		} else {
			$this->forget();
		}
	}

	//
	// Entities are the smallest recognized token,
	// and can be found inside a rule's value.
	//
	private function parseEntity() {
		return $this->MatchFuncs( array( 'parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment' ) );
	}

	//
	// A Rule terminator. Note that we use `peek()` to check for '}',
	// because the `block` rule will be expecting it, but we still need to make sure
	// it's there, if ';' was omitted.
	//
	private function parseEnd() {
		return $this->MatchChar( ';' ) || $this->PeekChar( '}' );
	}

	//
	// IE's alpha function
	//
	//	 alpha(opacity=88)
	//
	private function parseAlpha() {
		if ( !$this->MatchReg( '/\\G\(opacity=/i' ) ) {
			return;
		}

		$value = $this->MatchReg( '/\\G[0-9]+/' );
		if ( $value ) {
			$value = $value[0];
		} else {
			$value = $this->parseEntitiesVariable();
			if ( !$value ) {
				return;
			}
		}

		$this->expectChar( ')' );
		return $this->NewObj1( 'Less_Tree_Alpha', $value );
	}

	//
	// A Selector Element
	//
	//	 div
	//	 + h1
	//	 #socks
	//	 input[type="text"]
	//
	// Elements are the building blocks for Selectors,
	// they are made out of a `Combinator` (see combinator rule),
	// and an element name, such as a tag a class, or `*`.
	//
	private function parseElement() {
		$c = $this->parseCombinator();
		$index = $this->pos;

		$e = $this->match( array( '/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
			'#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly' ) );

		if ( is_null( $e ) ) {
			$this->save();
			if ( $this->MatchChar( '(' ) ) {
				if ( ( $v = $this->parseSelector() ) && $this->MatchChar( ')' ) ) {
					$e = $this->NewObj1( 'Less_Tree_Paren', $v );
					$this->forget();
				} else {
					$this->restore();
				}
			} else {
				$this->forget();
			}
		}

		if ( !is_null( $e ) ) {
			return $this->NewObj4( 'Less_Tree_Element', array( $c, $e, $index, $this->env->currentFileInfo ) );
		}
	}

	//
	// Combinators combine elements together, in a Selector.
	//
	// Because our parser isn't white-space sensitive, special care
	// has to be taken, when parsing the descendant combinator, ` `,
	// as it's an empty space. We have to check the previous character
	// in the input, to see if it's a ` ` character.
	//
	private function parseCombinator() {
		if ( $this->pos < $this->input_len ) {
			$c = $this->input[$this->pos];
			if ( $c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ) {

				$this->pos++;
				if ( $this->input[$this->pos] === '^' ) {
					$c = '^^';
					$this->pos++;
				}

				$this->skipWhitespace( 0 );

				return $c;
			}

			if ( $this->pos > 0 && $this->isWhitespace( -1 ) ) {
				return ' ';
			}
		}
	}

	//
	// A CSS selector (see selector below)
	// with less extensions e.g. the ability to extend and guard
	//
	private function parseLessSelector() {
		return $this->parseSelector( true );
	}

	//
	// A CSS Selector
	//
	//	 .class > div + h1
	//	 li a:hover
	//
	// Selectors are made out of one or more Elements, see above.
	//
	private function parseSelector( $isLess = false ) {
		$elements = array();
		$extendList = array();
		$condition = null;
		$when = false;
		$extend = false;
		$e = null;
		$c = null;
		$index = $this->pos;

		while ( ( $isLess && ( $extend = $this->parseExtend() ) ) || ( $isLess && ( $when = $this->MatchReg( '/\\Gwhen/' ) ) ) || ( $e = $this->parseElement() ) ) {
			if ( $when ) {
				$condition = $this->expect( 'parseConditions', 'expected condition' );
			} elseif ( $condition ) {
				// error("CSS guard can only be used at the end of selector");
			} elseif ( $extend ) {
				$extendList = array_merge( $extendList, $extend );
			} else {
				// if( count($extendList) ){
				//error("Extend can only be used at the end of selector");
				//}
				if ( $this->pos < $this->input_len ) {
					$c = $this->input[ $this->pos ];
				}
				$elements[] = $e;
				$e = null;
			}

			if ( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')' ) { break;
			}
		}

		if ( $elements ) {
			return $this->NewObj5( 'Less_Tree_Selector', array( $elements, $extendList, $condition, $index, $this->env->currentFileInfo ) );
		}
		if ( $extendList ) {
			$this->Error( 'Extend must be used to extend a selector, it cannot be used on its own' );
		}
	}

	private function parseTag() {
		return ( $tag = $this->MatchReg( '/\\G[A-Za-z][A-Za-z-]*[0-9]?/' ) ) ? $tag : $this->MatchChar( '*' );
	}

	private function parseAttribute() {
		$val = null;

		if ( !$this->MatchChar( '[' ) ) {
			return;
		}

		$key = $this->parseEntitiesVariableCurly();
		if ( !$key ) {
			$key = $this->expect( '/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/' );
		}

		$op = $this->MatchReg( '/\\G[|~*$^]?=/' );
		if ( $op ) {
			$val = $this->match( array( 'parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly' ) );
		}

		$this->expectChar( ']' );

		return $this->NewObj3( 'Less_Tree_Attribute', array( $key, $op === null ? null : $op[0], $val ) );
	}

	//
	// The `block` rule is used by `ruleset` and `mixin.definition`.
	// It's a wrapper around the `primary` rule, with added `{}`.
	//
	private function parseBlock() {
		if ( $this->MatchChar( '{' ) ) {
			$content = $this->parsePrimary();
			if ( $this->MatchChar( '}' ) ) {
				return $content;
			}
		}
	}

	private function parseBlockRuleset() {
		$block = $this->parseBlock();

		if ( $block ) {
			$block = $this->NewObj2( 'Less_Tree_Ruleset', array( null, $block ) );
		}

		return $block;
	}

	private function parseDetachedRuleset() {
		$blockRuleset = $this->parseBlockRuleset();
		if ( $blockRuleset ) {
			return $this->NewObj1( 'Less_Tree_DetachedRuleset', $blockRuleset );
		}
	}

	//
	// div, .class, body > p {...}
	//
	private function parseRuleset() {
		$selectors = array();

		$this->save();

		while ( true ) {
			$s = $this->parseLessSelector();
			if ( !$s ) {
				break;
			}
			$selectors[] = $s;
			$this->parseComments();

			if ( $s->condition && count( $selectors ) > 1 ) {
				$this->Error( 'Guards are only currently allowed on a single selector.' );
			}

			if ( !$this->MatchChar( ',' ) ) {
				break;
			}
			if ( $s->condition ) {
				$this->Error( 'Guards are only currently allowed on a single selector.' );
			}
			$this->parseComments();
		}

		if ( $selectors ) {
			$rules = $this->parseBlock();
			if ( is_array( $rules ) ) {
				$this->forget();
				return $this->NewObj2( 'Less_Tree_Ruleset', array( $selectors, $rules ) ); // Less_Environment::$strictImports
			}
		}

		// Backtrack
		$this->furthest = $this->pos;
		$this->restore();
	}

	/**
	 * Custom less.php parse function for finding simple name-value css pairs
	 * ex: width:100px;
	 *
	 */
	private function parseNameValue() {
		$index = $this->pos;
		$this->save();

		// $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
		$match = $this->MatchReg( '/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/' );
		if ( $match ) {

			if ( $match[4] == '}' ) {
				$this->pos = $index + strlen( $match[0] ) - 1;
			}

			if ( $match[3] ) {
				$match[2] .= ' !important';
			}

			return $this->NewObj4( 'Less_Tree_NameValue', array( $match[1], $match[2], $index, $this->env->currentFileInfo ) );
		}

		$this->restore();
	}

	private function parseRule( $tryAnonymous = null ) {
		$merge = false;
		$startOfRule = $this->pos;

		$c = $this->input[$this->pos];
		if ( $c === '.' || $c === '#' || $c === '&' ) {
			return;
		}

		$this->save();
		$name = $this->MatchFuncs( array( 'parseVariable','parseRuleProperty' ) );

		if ( $name ) {

			$isVariable = is_string( $name );

			$value = null;
			if ( $isVariable ) {
				$value = $this->parseDetachedRuleset();
			}

			$important = null;
			if ( !$value ) {

				// prefer to try to parse first if its a variable or we are compressing
				// but always fallback on the other one
				//if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
				if ( !$tryAnonymous && ( Less_Parser::$options['compress'] || $isVariable ) ) {
					$value = $this->MatchFuncs( array( 'parseValue','parseAnonymousValue' ) );
				} else {
					$value = $this->MatchFuncs( array( 'parseAnonymousValue','parseValue' ) );
				}

				$important = $this->parseImportant();

				// a name returned by this.ruleProperty() is always an array of the form:
				// [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
				// where each item is a tree.Keyword or tree.Variable
				if ( !$isVariable && is_array( $name ) ) {
					$nm = array_pop( $name );
					if ( $nm->value ) {
						$merge = $nm->value;
					}
				}
			}

			if ( $value && $this->parseEnd() ) {
				$this->forget();
				return $this->NewObj6( 'Less_Tree_Rule', array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo ) );
			} else {
				$this->furthest = $this->pos;
				$this->restore();
				if ( $value && !$tryAnonymous ) {
					return $this->parseRule( true );
				}
			}
		} else {
			$this->forget();
		}
	}

	function parseAnonymousValue() {
		if ( preg_match( '/\\G([^@+\/\'"*`(;{}-]*);/', $this->input, $match, 0, $this->pos ) ) {
			$this->pos += strlen( $match[1] );
			return $this->NewObj1( 'Less_Tree_Anonymous', $match[1] );
		}
	}

	//
	// An @import directive
	//
	//	 @import "lib";
	//
	// Depending on our environment, importing is done differently:
	// In the browser, it's an XHR request, in Node, it would be a
	// file-system operation. The function used for importing is
	// stored in `import`, which we pass to the Import constructor.
	//
	private function parseImport() {
		$this->save();

		$dir = $this->MatchReg( '/\\G@import?\s+/' );

		if ( $dir ) {
			$options = $this->parseImportOptions();
			$path = $this->MatchFuncs( array( 'parseEntitiesQuoted','parseEntitiesUrl' ) );

			if ( $path ) {
				$features = $this->parseMediaFeatures();
				if ( $this->MatchChar( ';' ) ) {
					if ( $features ) {
						$features = $this->NewObj1( 'Less_Tree_Value', $features );
					}

					$this->forget();
					return $this->NewObj5( 'Less_Tree_Import', array( $path, $features, $options, $this->pos, $this->env->currentFileInfo ) );
				}
			}
		}

		$this->restore();
	}

	private function parseImportOptions() {
		$options = array();

		// list of options, surrounded by parens
		if ( !$this->MatchChar( '(' ) ) {
			return $options;
		}
		do{
			$optionName = $this->parseImportOption();
			if ( $optionName ) {
				$value = true;
				switch ( $optionName ) {
					case "css":
						$optionName = "less";
						$value = false;
						break;
					case "once":
						$optionName = "multiple";
						$value = false;
						break;
				}
				$options[$optionName] = $value;
				if ( !$this->MatchChar( ',' ) ) { break;
				}
			}
		}while ( $optionName );
		$this->expectChar( ')' );
		return $options;
	}

	private function parseImportOption() {
		$opt = $this->MatchReg( '/\\G(less|css|multiple|once|inline|reference|optional)/' );
		if ( $opt ) {
			return $opt[1];
		}
	}

	private function parseMediaFeature() {
		$nodes = array();

		do{
			$e = $this->MatchFuncs( array( 'parseEntitiesKeyword','parseEntitiesVariable' ) );
			if ( $e ) {
				$nodes[] = $e;
			} elseif ( $this->MatchChar( '(' ) ) {
				$p = $this->parseProperty();
				$e = $this->parseValue();
				if ( $this->MatchChar( ')' ) ) {
					if ( $p && $e ) {
						$r = $this->NewObj7( 'Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true ) );
						$nodes[] = $this->NewObj1( 'Less_Tree_Paren', $r );
					} elseif ( $e ) {
						$nodes[] = $this->NewObj1( 'Less_Tree_Paren', $e );
					} else {
						return null;
					}
				} else return null;
			}
		} while ( $e );

		if ( $nodes ) {
			return $this->NewObj1( 'Less_Tree_Expression', $nodes );
		}
	}

	private function parseMediaFeatures() {
		$features = array();

		do{
			$e = $this->parseMediaFeature();
			if ( $e ) {
				$features[] = $e;
				if ( !$this->MatchChar( ',' ) ) break;
			} else {
				$e = $this->parseEntitiesVariable();
				if ( $e ) {
					$features[] = $e;
					if ( !$this->MatchChar( ',' ) ) break;
				}
			}
		} while ( $e );

		return $features ? $features : null;
	}

	private function parseMedia() {
		if ( $this->MatchReg( '/\\G@media/' ) ) {
			$features = $this->parseMediaFeatures();
			$rules = $this->parseBlock();

			if ( is_array( $rules ) ) {
				return $this->NewObj4( 'Less_Tree_Media', array( $rules, $features, $this->pos, $this->env->currentFileInfo ) );
			}
		}
	}

	//
	// A CSS Directive
	//
	// @charset "utf-8";
	//
	private function parseDirective() {
		if ( !$this->PeekChar( '@' ) ) {
			return;
		}

		$rules = null;
		$index = $this->pos;
		$hasBlock = true;
		$hasIdentifier = false;
		$hasExpression = false;
		$hasUnknown = false;

		$value = $this->MatchFuncs( array( 'parseImport','parseMedia' ) );
		if ( $value ) {
			return $value;
		}

		$this->save();

		$name = $this->MatchReg( '/\\G@[a-z-]+/' );

		if ( !$name ) return;
		$name = $name[0];

		$nonVendorSpecificName = $name;
		$pos = strpos( $name, '-', 2 );
		if ( $name[1] == '-' && $pos > 0 ) {
			$nonVendorSpecificName = "@" . substr( $name, $pos + 1 );
		}

		switch ( $nonVendorSpecificName ) {
			/*
			case "@font-face":
			case "@viewport":
			case "@top-left":
			case "@top-left-corner":
			case "@top-center":
			case "@top-right":
			case "@top-right-corner":
			case "@bottom-left":
			case "@bottom-left-corner":
			case "@bottom-center":
			case "@bottom-right":
			case "@bottom-right-corner":
			case "@left-top":
			case "@left-middle":
			case "@left-bottom":
			case "@right-top":
			case "@right-middle":
			case "@right-bottom":
			hasBlock = true;
			break;
			*/
			case "@charset":
				$hasIdentifier = true;
				$hasBlock = false;
				break;
			case "@namespace":
				$hasExpression = true;
				$hasBlock = false;
				break;
			case "@keyframes":
				$hasIdentifier = true;
				break;
			case "@host":
			case "@page":
			case "@document":
			case "@supports":
				$hasUnknown = true;
				break;
		}

		if ( $hasIdentifier ) {
			$value = $this->parseEntity();
			if ( !$value ) {
				$this->error( "expected " . $name . " identifier" );
			}
		} else if ( $hasExpression ) {
			$value = $this->parseExpression();
			if ( !$value ) {
				$this->error( "expected " . $name. " expression" );
			}
		} else if ( $hasUnknown ) {

			$value = $this->MatchReg( '/\\G[^{;]+/' );
			if ( $value ) {
				$value = $this->NewObj1( 'Less_Tree_Anonymous', trim( $value[0] ) );
			}
		}

		if ( $hasBlock ) {
			$rules = $this->parseBlockRuleset();
		}

		if ( $rules || ( !$hasBlock && $value && $this->MatchChar( ';' ) ) ) {
			$this->forget();
			return $this->NewObj5( 'Less_Tree_Directive', array( $name, $value, $rules, $index, $this->env->currentFileInfo ) );
		}

		$this->restore();
	}

	//
	// A Value is a comma-delimited list of Expressions
	//
	//	 font-family: Baskerville, Georgia, serif;
	//
	// In a Rule, a Value represents everything after the `:`,
	// and before the `;`.
	//
	private function parseValue() {
		$expressions = array();

		do{
			$e = $this->parseExpression();
			if ( $e ) {
				$expressions[] = $e;
				if ( !$this->MatchChar( ',' ) ) {
					break;
				}
			}
		}while ( $e );

		if ( $expressions ) {
			return $this->NewObj1( 'Less_Tree_Value', $expressions );
		}
	}

	private function parseImportant() {
		if ( $this->PeekChar( '!' ) && $this->MatchReg( '/\\G! *important/' ) ) {
			return ' !important';
		}
	}

	private function parseSub() {
		if ( $this->MatchChar( '(' ) ) {
			$a = $this->parseAddition();
			if ( $a ) {
				$this->expectChar( ')' );
				return $this->NewObj2( 'Less_Tree_Expression', array( array( $a ), true ) ); // instead of $e->parens = true so the value is cached
			}
		}
	}

	/**
	 * Parses multiplication operation
	 *
	 * @return Less_Tree_Operation|null
	 */
	function parseMultiplication() {
		$return = $m = $this->parseOperand();
		if ( $return ) {
			while ( true ) {

				$isSpaced = $this->isWhitespace( -1 );

				if ( $this->PeekReg( '/\\G\/[*\/]/' ) ) {
					break;
				}

				$op = $this->MatchChar( '/' );
				if ( !$op ) {
					$op = $this->MatchChar( '*' );
					if ( !$op ) {
						break;
					}
				}

				$a = $this->parseOperand();

				if ( !$a ) { break;
				}

				$m->parensInOp = true;
				$a->parensInOp = true;
				$return = $this->NewObj3( 'Less_Tree_Operation', array( $op, array( $return, $a ), $isSpaced ) );
			}
		}
		return $return;

	}

	/**
	 * Parses an addition operation
	 *
	 * @return Less_Tree_Operation|null
	 */
	private function parseAddition() {
		$return = $m = $this->parseMultiplication();
		if ( $return ) {
			while ( true ) {

				$isSpaced = $this->isWhitespace( -1 );

				$op = $this->MatchReg( '/\\G[-+]\s+/' );
				if ( $op ) {
					$op = $op[0];
				} else {
					if ( !$isSpaced ) {
						$op = $this->match( array( '#+','#-' ) );
					}
					if ( !$op ) {
						break;
					}
				}

				$a = $this->parseMultiplication();
				if ( !$a ) {
					break;
				}

				$m->parensInOp = true;
				$a->parensInOp = true;
				$return = $this->NewObj3( 'Less_Tree_Operation', array( $op, array( $return, $a ), $isSpaced ) );
			}
		}

		return $return;
	}

	/**
	 * Parses the conditions
	 *
	 * @return Less_Tree_Condition|null
	 */
	private function parseConditions() {
		$index = $this->pos;
		$return = $a = $this->parseCondition();
		if ( $a ) {
			while ( true ) {
				if ( !$this->PeekReg( '/\\G,\s*(not\s*)?\(/' ) || !$this->MatchChar( ',' ) ) {
					break;
				}
				$b = $this->parseCondition();
				if ( !$b ) {
					break;
				}

				$return = $this->NewObj4( 'Less_Tree_Condition', array( 'or', $return, $b, $index ) );
			}
			return $return;
		}
	}

	private function parseCondition() {
		$index = $this->pos;
		$negate = false;
		$c = null;

		if ( $this->MatchReg( '/\\Gnot/' ) ) $negate = true;
		$this->expectChar( '(' );
		$a = $this->MatchFuncs( array( 'parseAddition','parseEntitiesKeyword','parseEntitiesQuoted' ) );

		if ( $a ) {
			$op = $this->MatchReg( '/\\G(?:>=|<=|=<|[<=>])/' );
			if ( $op ) {
				$b = $this->MatchFuncs( array( 'parseAddition','parseEntitiesKeyword','parseEntitiesQuoted' ) );
				if ( $b ) {
					$c = $this->NewObj5( 'Less_Tree_Condition', array( $op[0], $a, $b, $index, $negate ) );
				} else {
					$this->Error( 'Unexpected expression' );
				}
			} else {
				$k = $this->NewObj1( 'Less_Tree_Keyword', 'true' );
				$c = $this->NewObj5( 'Less_Tree_Condition', array( '=', $a, $k, $index, $negate ) );
			}
			$this->expectChar( ')' );
			return $this->MatchReg( '/\\Gand/' ) ? $this->NewObj3( 'Less_Tree_Condition', array( 'and', $c, $this->parseCondition() ) ) : $c;
		}
	}

	/**
	 * An operand is anything that can be part of an operation,
	 * such as a Color, or a Variable
	 *
	 */
	private function parseOperand() {
		$negate = false;
		$offset = $this->pos + 1;
		if ( $offset >= $this->input_len ) {
			return;
		}
		$char = $this->input[$offset];
		if ( $char === '@' || $char === '(' ) {
			$negate = $this->MatchChar( '-' );
		}

		$o = $this->MatchFuncs( array( 'parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall' ) );

		if ( $negate ) {
			$o->parensInOp = true;
			$o = $this->NewObj1( 'Less_Tree_Negative', $o );
		}

		return $o;
	}

	/**
	 * Expressions either represent mathematical operations,
	 * or white-space delimited Entities.
	 *
	 *	 1px solid black
	 * @var * 2
	 *
	 * @return Less_Tree_Expression|null
	 */
	private function parseExpression() {
		$entities = array();

		do{
			$e = $this->MatchFuncs( array( 'parseAddition','parseEntity' ) );
			if ( $e ) {
				$entities[] = $e;
				// operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
				if ( !$this->PeekReg( '/\\G\/[\/*]/' ) ) {
					$delim = $this->MatchChar( '/' );
					if ( $delim ) {
						$entities[] = $this->NewObj1( 'Less_Tree_Anonymous', $delim );
					}
				}
			}
		}while ( $e );

		if ( $entities ) {
			return $this->NewObj1( 'Less_Tree_Expression', $entities );
		}
	}

	/**
	 * Parse a property
	 * eg: 'min-width', 'orientation', etc
	 *
	 * @return string
	 */
	private function parseProperty() {
		$name = $this->MatchReg( '/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/' );
		if ( $name ) {
			return $name[1];
		}
	}

	/**
	 * Parse a rule property
	 * eg: 'color', 'width', 'height', etc
	 *
	 * @return string
	 */
	private function parseRuleProperty() {
		$offset = $this->pos;
		$name = array();
		$index = array();
		$length = 0;

		$this->rulePropertyMatch( '/\\G(\*?)/', $offset, $length, $index, $name );
		while ( $this->rulePropertyMatch( '/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name ) ); // !

		if ( ( count( $name ) > 1 ) && $this->rulePropertyMatch( '/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name ) ) {
			// at last, we have the complete match now. move forward,
			// convert name particles to tree objects and return:
			$this->skipWhitespace( $length );

			if ( $name[0] === '' ) {
				array_shift( $name );
				array_shift( $index );
			}
			foreach ( $name as $k => $s ) {
				if ( !$s || $s[0] !== '@' ) {
					$name[$k] = $this->NewObj1( 'Less_Tree_Keyword', $s );
				} else {
					$name[$k] = $this->NewObj3( 'Less_Tree_Variable', array( '@' . substr( $s, 2, -1 ), $index[$k], $this->env->currentFileInfo ) );
				}
			}
			return $name;
		}

	}

	private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ) {
		preg_match( $re, $this->input, $a, 0, $offset );
		if ( $a ) {
			$index[] = $this->pos + $length;
			$length += strlen( $a[0] );
			$offset += strlen( $a[0] );
			$name[] = $a[1];
			return true;
		}
	}

	public static function serializeVars( $vars ) {
		$s = '';

		foreach ( $vars as $name => $value ) {
			$s .= ( ( $name[0] === '@' ) ? '' : '@' ) . $name .': '. $value . ( ( substr( $value, -1 ) === ';' ) ? '' : ';' );
		}

		return $s;
	}

	/**
	 * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
	 *
	 * @param string $b
	 */
	public static function is_method( $a, $b ) {
		return is_object( $a ) && method_exists( $a, $b );
	}

	/**
	 * Round numbers similarly to javascript
	 * eg: 1.499999 to 1 instead of 2
	 *
	 */
	public static function round( $i, $precision = 0 ) {
		$precision = pow( 10, $precision );
		$i = $i * $precision;

		$ceil = ceil( $i );
		$floor = floor( $i );
		if ( ( $ceil - $i ) <= ( $i - $floor ) ) {
			return $ceil / $precision;
		} else {
			return $floor / $precision;
		}
	}

	/**
	 * Create Less_Tree_* objects and optionally generate a cache string
	 *
	 * @return mixed
	 */
	public function NewObj0( $class ) {
		$obj = new $class();
		if ( $this->CacheEnabled() ) {
			$obj->cache_string = ' new '.$class.'()';
		}
		return $obj;
	}

	public function NewObj1( $class, $arg ) {
		$obj = new $class( $arg );
		if ( $this->CacheEnabled() ) {
			$obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString( $arg ).')';
		}
		return $obj;
	}

	public function NewObj2( $class, $args ) {
		$obj = new $class( $args[0], $args[1] );
		if ( $this->CacheEnabled() ) {
			$this->ObjCache( $obj, $class, $args );
		}
		return $obj;
	}

	public function NewObj3( $class, $args ) {
		$obj = new $class( $args[0], $args[1], $args[2] );
		if ( $this->CacheEnabled() ) {
			$this->ObjCache( $obj, $class, $args );
		}
		return $obj;
	}

	public function NewObj4( $class, $args ) {
		$obj = new $class( $args[0], $args[1], $args[2], $args[3] );
		if ( $this->CacheEnabled() ) {
			$this->ObjCache( $obj, $class, $args );
		}
		return $obj;
	}

	public function NewObj5( $class, $args ) {
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
		if ( $this->CacheEnabled() ) {
			$this->ObjCache( $obj, $class, $args );
		}
		return $obj;
	}

	public function NewObj6( $class, $args ) {
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
		if ( $this->CacheEnabled() ) {
			$this->ObjCache( $obj, $class, $args );
		}
		return $obj;
	}

	public function NewObj7( $class, $args ) {
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
		if ( $this->CacheEnabled() ) {
			$this->ObjCache( $obj, $class, $args );
		}
		return $obj;
	}

	// caching
	public function ObjCache( $obj, $class, $args = array() ) {
		$obj->cache_string = ' new '.$class.'('. self::ArgCache( $args ).')';
	}

	public function ArgCache( $args ) {
		return implode( ',', array_map( array( 'Less_Parser','ArgString' ), $args ) );
	}

	/**
	 * Convert an argument to a string for use in the parser cache
	 *
	 * @return string
	 */
	public static function ArgString( $arg ) {
		$type = gettype( $arg );

		if ( $type === 'object' ) {
			$string = $arg->cache_string;
			unset( $arg->cache_string );
			return $string;

		} elseif ( $type === 'array' ) {
			$string = ' Array(';
			foreach ( $arg as $k => $a ) {
				$string .= var_export( $k, true ).' => '.self::ArgString( $a ).',';
			}
			return $string . ')';
		}

		return var_export( $arg, true );
	}

	public function Error( $msg ) {
		throw new Less_Exception_Parser( $msg, null, $this->furthest, $this->env->currentFileInfo );
	}

	public static function WinPath( $path ) {
		return str_replace( '\\', '/', $path );
	}

	public static function AbsPath( $path, $winPath = false ) {
		if ( strpos( $path, '//' ) !== false && preg_match( '_^(https?:)?//\\w+(\\.\\w+)+/\\w+_i', $path ) ) {
			return $winPath ? '' : false;
		} else {
			$path = realpath( $path );
			if ( $winPath ) {
				$path = self::WinPath( $path );
			}
			return $path;
		}
	}

	public function CacheEnabled() {
		return ( Less_Parser::$options['cache_method'] && ( Less_Cache::$cache_dir || ( Less_Parser::$options['cache_method'] == 'callback' ) ) );
	}

}