From c9c0fb28ee9331ad85d8b05926a879f277afc26f Mon Sep 17 00:00:00 2001 From: Rob Blanckaert Date: Sat, 2 Feb 2019 10:56:49 -0800 Subject: [PATCH] Counter expressions (#3534) * Add peglib * - Add expression engine - Take an expression when setting a counter * Shift + Click = Middleclick for counters * minor cleanup for clangify Signed-off-by: Zach Halpern * Added tip entry --- clangify.sh | 1 + cockatrice/cockatrice.qrc | 1 + .../tips/images/counter_expression.png | Bin 0 -> 44830 bytes cockatrice/resources/tips/tips_of_the_day.xml | 6 + cockatrice/src/abstractcounter.cpp | 22 +- cockatrice/src/abstractcounter.h | 26 +- cockatrice/src/dlg_tip_of_the_day.cpp | 10 +- common/CMakeLists.txt | 1 + common/expression.cpp | 108 + common/expression.h | 28 + common/lib/peglib.h | 3293 +++++++++++++++++ tests/CMakeLists.txt | 8 + tests/expression_test.cpp | 30 + 13 files changed, 3508 insertions(+), 26 deletions(-) create mode 100644 cockatrice/resources/tips/images/counter_expression.png create mode 100644 common/expression.cpp create mode 100644 common/expression.h create mode 100644 common/lib/peglib.h create mode 100644 tests/expression_test.cpp diff --git a/clangify.sh b/clangify.sh index 9113952e..671c69b9 100755 --- a/clangify.sh +++ b/clangify.sh @@ -14,6 +14,7 @@ include=("common" \ "servatrice/src") exclude=("servatrice/src/smtp" \ "common/sfmt" \ +"common/lib" \ "oracle/src/zip" \ "oracle/src/lzma" \ "oracle/src/qt-json") diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index 3619cc0d..aff2f002 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -351,6 +351,7 @@ resources/tips/images/cockatrice_register.png resources/tips/images/cockatrice_wiki.png resources/tips/images/coin_flip.png + resources/tips/images/counter_expression.png resources/tips/images/face_down.png resources/tips/images/filter_games.png resources/tips/images/github_logo.png diff --git a/cockatrice/resources/tips/images/counter_expression.png b/cockatrice/resources/tips/images/counter_expression.png new file mode 100644 index 0000000000000000000000000000000000000000..0e6bbcefe93cfb2747aff3b4e84492d33585f086 GIT binary patch literal 44830 zcmeFYbyQs4mM@CCyHiNe6z=Zst|3U_?oMzC9$bP;2<{FE5-hm8ySoIqm3*hq`Odrj z`o13H-TxX!)!2KjIe&AmX-oF5NEIb%R3suK2nYyNSs4j62nZ-F@O32u9QfO`YC zf{@8uL&sIk*aP6`>|kzX3j(-$If4KnPb+f>2+zfejDs3#ZuijF9;EaS&rrMXW8&f2 zjMhXoxfje_Xr%J|xAkkw;=2t;p$hq5uh!x{Hsj}H*n&>!n%k?I%dR-%IpH6_3nho9S319L3o9<>Ftlv5%)F^V% z8V!e%QBHJHgp*1&Z?xwyzAaAMitHaF&{jCplgINhV5ZkK+K{3WTq=^H!PAvDh!x($ zTJ?KsTSJQBKkMcZdE=7nquEM9msTj`i$$V2r68U@<*_O$_WilP zzWu(F`HsCJ;Fr1yHsM?5kr-GsVPWwRte4V?>&q-pql<+HrE_p3loz3 zuB~O&`^?mF>f3m*GWsfOe>D3~f#LEg^Gv^3$%pdxp|W8ueZLq|kHz}e%B5xdwlk;i zc}Yq_JJY5m+5YS)huX#;rb*XWg-jX>eytEoKmA8r7IoSR8?e|>fV+#6 zX@g+&)95eoLn#9z;RX6NC)F0eD@&7M#!zq_n)Zne6d zy=~KtG|dtkLJ6OF`@Oq*HTh(W;4UkpIPPtF{k??zT9VYtgj9pz9idxdLa(hPa_@XU z?8)0SMgG*GO8OLD2NGH@dC+l>oPJ#D+N?}Rm3@nz{#5k1I#jCV+|n<->EQv|8N(pr zZV5|MCFF3)R2Q1av~b%QDY?L11-DP@OG^}9rs!6&H849|N@V=Z!}bkanX28m;|>Lw zuE=K7YbFy+^+~431!OM^X&@C}Kh{qe=J_1p|v7yq%{3Ev!93lj~=I5Ox4xi$& z-Y|G4R{3y|V(W5Zn3pd;ed6fR@i=5UR!FfKY+_SdIQL=liC&SL|CuuntXyy)_s~() zP_M6xL3a6c=qK&Kzr4FBnEo*5=6)=clXgmSKo_PRnsk^O?Of@hS5cER@?9pMQ+Wzk z-!;tPE1&9$phSO;(!qIby)_?$owxn;WMe||f+dc(J*SxZk371m&g#L>N%{moff>xF zK0}LwV_N7yq+%wHzMAY&U}-+k3wmd@x8q>%TYXD?Y#_xnV9L?eCysY*+!_gI3K0ey zg7BfgZclL5xdq>tn~xp;JAF~fwihPM-R%??aJ^Tk6T*En1t%6zr103qqiX)p}eVjI)y zS%n4D1LSr*Zt8cd&gsUzAw}fl5|ze7$D?5-28187(tQ#?wIQLuNjo*NPZm~36J;0h z+tiT_y-eovBgMx(m=^wMCPYGrr~I^2%BEIGHG9yaQFN?x_MzLL4#IX?M+rwec2=j_ zZ8fraSKnw-Etha&v3_+va1?dKyJ+CKA6V)-l)(j4w};m8Q`E0KDd7MT!7e;e>zBCd zxN+v@RJ%<$M?uL5M28x`rpYp_0^!Wbq;$Lgr)nes7W#lOt~LILizG4}6+#tn#;R3i zv}o*u)e&lTzotD1N)jNBOC+eTV^XIf#d&c^;*BCyS0jNJ!<^tOvMkBZqHg7we3)Zn zop-tl`{9MwC;qG#_ z8jBLIX2kp27^Kc79UJDJJy7S#9~yqrZq$?=ZpO{MmWU1ikc?{VR^eFGA;@%1JIsd` z7tVZ_hI7kvqo!wLs6zE&gG!}YrJT<4OQpV3y#@66L0hMTp4kn8h1Gm@AggfZhgfGj z3uyQXY5-=us+CODFb-?nw)Z+J1q2R@S6&|fqbKvc$BM!1;9WgmUDaE5B?2?GB`YY; z<#u^T4O<9niLN(}rrLwL1L?~VG7M2>RQlecB~vAEg19RA$zPAnbxVWhxvNbnXy^yT zv&-)45LlOq_%NV*Ryh}M8?d#^A}JyHa^H9+XMLA5V0~-dVGXwjN` zAuoyCHv&wM>s+Cby&P67O@()kfKjf=qo8$e9D(t(-Ddg>Tdo~6Xr-}jKA$oQ$9IMn z9vc(C-cXAo)hUS>a%5;zw59ajCaLlB|(i0oBVwnR7Q zWFW1$b-!hEs$Eg(Jr6rof!`5P?QLLykcv!#83PIhk`?>2Bonf9M^jk=<2lDI8yKJC z#c7>$$wQN-QA$PpyBUD!;X6`lMTnf(MIPv_aQZG_ZIP1N8$gTQyV#X`>`beAb6P zRR(Bpn3!OfHfSf1Bc4+a=q|%pQ^sZw>Qp;;nr1u}4Gzv*tL29xlc)|uDdYiGZ|;bs zhS~{e3T+|}eaystap(Kp1zE0#Pjl({#?o{eGKsQ`prQMC z8)F#({p!w!0Rk|X?eI2IWt@gk&x)#k-yKV~uLOAt#|rH_>>DXs9&h}_&vD8o6T5XD z9405j;`#hn42JHHuo?9~k|)Rsc>5)Ue6~AAh(F_vd=TDGL{q)NxnSg%)e4>%sA%Q) zqECcGby25@M%lC-Pjj01a&9P&xG`th^`@&}MU;9WVn9G(_m3y%{0CyizDb7AEp5BJ3VLv{~DqwP*~$$C3&i45ke6Li;wo} za^R`7+uPR9+f7jYqU-xBVTrkd+#~|iLX8mHeoVd-<`T^bn7tIs3%6lH#XW0Qsu+2x zZ;@Kq3GtpoSi8jpWca@m5^Z3^+Cf(IvA^Nzo42wGn*zBp+oq}ySzMu7Sd~b{*90%n zjl!(CRMkk%%4nBMD@c6Q3Z+r{Hi9P~JJ(7e@vgajt+T4fwmOQGC()#6ODnWE??=WL z33}70hGmOJ%oDUKLqKf#^H#UngkO1aAV21J=nFLK81wRT0vbH0MO}`8Y4z%4+H;NZ zH^biq2sJQF!~{&;uu$&x@?VJ{WRR(dy=2=^B9^|k0-nFt@G&IAsxPM&cUUK~ec{!U zBc@4Rx}yE=E!^fz+?kx%cYME7qljp{SkwEfpqRqatk-?-yWsRTC5~rrVV>jo zZ&2-emPe`z?_WsJ?2HaYyvj?EYGx&ldR5jiF32>HE3p=(9#yLtGWj*^P&7qFEg>8v z3+;qY2i3^Ql^x1-C{E_OMJ$aod)0{|y;LOP=U zD*2FR=n;8ydTY0YIk$sV9W4CGdRbPOex-y0NTNP10OyJ>V^KJIT-({=WCQBA0U|0h z06dGlH~b8kdWz>nWM4%&Po$wg!x_9mxx9jU*X>(xA+gbeJ|GrtUlMK~rgjidlZA#} zr{4q3);t>pH<*a#ZZ$)~qidLj= z@~3t*fAsQF0pmCJ9Wk`>-xU5~Vcp-OwR%X)rF@KVesHTVg&f@`TqxuTDb-PcF4 zB(|27pYXU01@WpU{h5|Msw`aqG7aOWhun17R(LFx2gQ1QRbKPQK8E;`cT-x-ilXET zbxL>YO(*J~_tZbH^OC(X7ZeFsHU~6+8@p)mdpteAy9gj$d3L`K;C^&4KYRIf@Y-`< zt2$$>#$Xr`3txXu=$Vp2yxh>91}Fs)Z${_HP_aQ@5^vg;`NEUTn6%l)^C>370djG-}gmaSx!Ml5AJCUfbQP#lH@)dtV81NmeR2XG^jJni5vbTjN@XY zFksZC)pr-yfo%=WK@y2mi>KC+f7io_$V`cW1XzqWfo!S^Ye(m~cMh9{INv_RR)e?+ z)gdo`&Vu|!@r%|R?!Ao~D+hbNuV!_)%tRFjYzl-d`OZR1jMX@F;~+uq&-A+3&!b_8Mm3X zvZX6h9b{EW8SN*H$jRX`_=>?i2LGvG|a5Cs2OZ=^dskQRG|~Js%9Mc0(-< zA&mleL4okMi=vsr%?D2`?@!{!A6Yw$%){4@of;VC3(%nP723!aw~9=MrQ6>QoV|rBQAkuOudhQVQ0~IQK;`6@J0XvJD78i) z28duvJ{mva(j&D*!TH#_%FANzDvUasCwp?1qBo#E%#J>N>B@Rf#m9TQKm4W=(}?!% zd(3uJJ;9nW1e`*mGUQ~0a{HAn%2mW!6iCEa4-_&N9D&cMTQE{g2ikImv|O7<^Qy@o zRM+Tp=euDD;r3HI`{`0H(W3LLlcuKgrYEg1h*BfOXF3k)Y^k;}XK>WVmG-}ba?s+4 zg;3v^J(&t;k(}l|xSbBV|k6ui?w8 zc1-=~IY5^S_S2JbCq_0!imp%bBN=J_<^B`8!~m}7maNaNj9E~lTcb=>DgTGc3RFxx zOI`QRztCUN=JUC_3Vgkz$}Iq}b6@YM;#_5cl(2Z!kE8Sr4ZYlN;+vW|+@h)R(GXD{ z3HM~(LD<`dGRPBt?-ev?2HN)W^^Jj&|P9Jfy=~isOT*r0s9q3+(4`F53 zfvmB+*UZG_ND@)@{6<7benotgX>3aEQeIu0g0ri3Z;E<@nIlrs&UjFmRzFbL!XX$dOPCH-#XXiIZnh~!B7Qh<-aV2c02LLbq765DYQWp8V8PuL~j zkwssoiHXH5Cig@w5tgRzt@)DgRk$8}>EszsATggM2Mwj%5v>h!u=nhi41qv=YmD*h zwvQV|QnwGOSaPi;R=at=F=U?ynx(gr$Yp>D69!T!Z+Tt}qGM732W&*NOgo_UI#S8H z+XMZVVtbVmylf;FENkERGv0knWyP3HQ94OHTXJQI+NPhrySG_bd4)HYOF1bWu#vN6 zC6+JI)5IJ7nF-Mm%lcc&ZS(G4JR!c=(kj$hqpl%*aVH#hR2~jieH6j1S@E}Zl^(x1 zcC)yoRL)r!%z>{KccSUbj2ZO&(cj))@~L$rgxjD#xVmL1B~;fgr{4B|5O2OTK8Av< zPYsYAg;_{}b8DLIJ5doeGy`5~p7DJC{-iPWBR94d=Y2E{T*(ASF7{{HXD(`)j^TXr z#nw@;_v&Wn=u`T5Nmy`9_($7oXj{pJdYAK@aJCkt@eUZ*o~^?aYq}i@}KdSNv;`F=&nH(uCL7N$3uXQ!0A}IKpI~?7b;XR3)(;6jd5@fqHwO z>M+OokEyp#@(dJ^R?y}sF!Q;PlAp)X5h>w&5W@h#1>k@TS_+YXDm(`ZS*z@O@;c*6 zSDA@5ady-qQC-!Hh3n?f0$waXV_4+25c~)S^9ZErSvSIViR{!cmNjI$r zyK=yrz3r+An3mX}TGVnWxuVpj1+G4_b(a*M_yzd1fVIB-Gn6B{z*-Qci>U-c-9&{~I<@bW?$ugwBp3(22 z2AMS{Va$cMuCGp4{xs1wGsPg65x(0>f!dZHi{R8l?I}FY$&WFIigpscwL6)0fgRr# zV#apn)}DwXXFN)BD-PpYYi_ADjeP-^i{rvKX~h~upT+!{ZPe8lwLwCy0%&R3cy65H z&{e9cImR_p+fg^qbh8BorSbJdByRU81=cV!@eE1FPMhf&Vv{a{`=Qd)DOhIH-waHG zTncK9AGq^LfM?7(@Ql}V`&-23^(H=_Rs(XbYY^YDu|G{j7DuXzJ~0sXqCa%@Lmq>@Os9v?zxLDdLt(rzMgREu~4Eq)%U7m)$vCK#4?i8lD~^a*aDzKZ_v%sctES_n^ks`YNHq&U#5x2s_8PEbCsc3{%_ zlXjFGCB_aK_0hN3hlQGjMG7y7;kVSJuh(TySHr^ZF3xzv#vFs%jD+u+wAYYN$uw97F-@c1&`f_FWUiE-McAkPeQNDXxq~zy$9FD4Zb(%$A-as(b2g-h;oTh=M zQ4Ium9Bs!OzV;%zZc96-y%UorILmw|1TMnB```mY-l!(4)UWT>JGCr7lSvpJB$QU? znRvkhn4xgrr%o+!h*<=TRy$B$YGfU--XHICuTn3T|};d8)e+~;HAqvRTYAVe~8K)_JJFFv}T zV=JS-%*LN-zCFnrFblMFt!}-^QuaH;DD|30vl*Neh<%5DSfD9?CgU_x{Nt_n+z&_J z@G;p>Iu&vDH!OluI@*{p-j|!LD;*xM$S#`5vY`{pm=F-?Jyzf+CpwA>e5MX|OvYvo zCLktHJ4f)76$l6cVNXY6QyY*gzyxGrWiLp1*49M{urd>*)aF!VQFIgsSz5_>JA>4{ zl{8GfZA^L1D20WP1U&h`0Cpf(V}Pfft-TAMry%7YTt4vi?`CF7z#kG<8$n7PMHPU! zgEI)g!NkGD0+jT$a%ZCyLIMalo0;>eNl5(z0(>V(Y3b_f$j8j=;o-sL!OrC1Y{AUR z%gf8m!p6+T1_V<8UA*jFjXi<(E>yoE{=$#|xtKazIl5Xo*aLoJ8k;z{xe8KJg8Kpg z00;M)N_v7D{~_S_{U3N2S2Jc=@C^reJYWK5HWn5hAPXCijhFfF{NP?i#eZ_!yZpn7 zV0$uq8apzxGO;k*+5H=bi>svjzvTU=94;E*2UyH%AQuNWXH$@*JILOZ>hD4wZQWe{ zF4N5g^tc3N(nf{Z`(aqWR4~>~A zGsqTX2Uf%djLiCP^uHgp{gdP0#`C-9e^Uf3?mwsg4}1O5<&RzYBpgiLehZbA5TyLA zFQ1u%sg)VupG$5IV-SeNj0MQcX=V!K;AH~=c{oA5Ko)azR!&|MPEHVy$={%4?Oj}r z?M*?yp}^owR$v@<5SOtDw>dk|*p!P6$id3V2IS#kVF!X(&AGXZSy;KuL2Q46PaW??fi<1*m|vh(no138SjSb)ZyoE$(77Iu(1D;p~p3oq9nsNa6z z6IGEFq-0}a`B#gIt+A`QgR`9=rJ}JZKw14?JsMVaAaz&c-&SMgZ*lnes`25sksYW@Tq=0b+KvxA@cX+bw)5R-PbR9SJbF z{a>7%|G@iKukPP^*}4BxA3R<@ac7XRtAn$KgM+Og5gQ@ZF z#QQ@GGW(sA|Hw))V+-a#w*t)n74iRuq;BcpVgLUP=U=4%L=km%^>A>uQFc}~u?CsC z{?|DF8Sy`n)WG%7#nstM_Wv-b{|BAGUm-06=5=uP`kQ=pkkemxe-$lTt3Rd!0R9vT zK4a70RurUkF?I);{c#`Ij(>HTS{mD1fWQ^`AL;lX?N)EbzNjDJt^)-6#UTOBtW+ z?}$}*b9A(|0y+P?vHl}?{$J?+#{Z8%{Xen)o$N1eaR)~)aI#yvDtp-fm*W2u!oLs{ ztW3d)^Bjmqj z-~Tb!|CsB)Wr6<|@&82E|CsB)Wr6<|@&82E|8M3(`q$1JWDj2ad4Tub?9O%8;5|E> ziM+G~#Ov?>+>Vk@;1)zj8C@3$2)sAHKajF&)aT$vcvo3PN%$=&C?r^7cbRnp2nYa# ztc0kB=i+g;M;6Y`^N%%l{j*yJ4|r)|Z*3R}jlxnL$QpUun%;T&<2U_e^0tu~I#{O2 zHNTDxO(jJoisX{>Wm<-AI#$xX`~yZtPHo$kpeu97!si^@`0LomlJ+{*&i1@^2|+Kv z9)jExG4c&l><$D!7!z1#Nb!)+kf0S<5rr88LLzimA07)oDQylhR+){YvD!7RwQc$w z$f)96US94mHZTh1R4FzZ6TpE$prq8)f+M~zn4g~oGi@qc!UXS@>lSuqt8~8IxqO!Q zKVI7Or4b&q0+ui!F|4cM7?ed>8;l2EqJW? zEj1`L;nKFc9*EtYu3S7l+ts2P&Mh?B5id2`J@q-798P~(cAGPdCKv8p*wJEsH?sNT zW#V?P#*J3+|M#*l}f2@13mqIqZI68OYOrG0IPDO^L$# zf_^#*6@H6Ad8|N*gQTjXBEv#(%(Ev6hozQ%{)h+{96j$L{0W|X$@cbk{HNsL zr6bDq;r;?|gpU|Eif)z^*^8}2Wj0?2k+<3N&&bRy%+6{7l)5Z=M|>H@MX+yFwZe2* z&};VjlWuH&jy}|YE?MM zS4X3l?bha6LuL}CK#w!Jz-(RpUAc2n0de1Q`6JHF}MkA=aAu zTFw0wm!(k5?NSmPp)o2U9~h$3fOWArjPt7*&IkY@*qXs!CU52a1Y$@5O7b2OBvFV@ z@d@3a-Xx_Y2SE`M5^nGAN(FsvedIelJfve_po}M!vGMzOsC9l_{93DI_<@lhSy>ei zA~V?LzANMG-u}rWYxam*$^z9!a{J50)X>?zUb8E z0!2dvT3WbN6!W2wVE1TjV(qD~fT752S)R=WfT2A7V8nEk2hc;-VtA+(X*KJKNI2PH zeLB5s=R?gWRn0dULv*o>G4iB%Hs!4J}n)j`Sg_^oxo7oRDMx0$pfLdVuefCLM{% zRmx9Kzjsi9A_`*;eTVgGMn=k|^b)^|iFbwvO(RG0MJg(K9fc|4=PZLG9fpLh3Ob~X z3~M=}yY;9<kObvFk-q+6hX8AYWJMdgyEz6Wd&h zr-lkKoU{~NaWyq@pTLK?gc=Pn$E2=6^=wmcjJUZ=m6i=Pz$?=9?0j#i^P?8ii{Rev z{QiWzwmn_oKa9I)5I9^hS;)=f?;3jZM3aF?T`o>3qVxIiUhvlZ_`Ih5@cd} zuqzWwB8*%$yu^S{sp&XjQrEA2MSt&vDmP~$jbZF3O_xhR$NK<|q_C)}U8>l$)#Jmm zuU|!3&wj>+e=__m0uTWg(c;#ypr9c0xjW+>{Bm{t_2C2a(~lICvW58wAxL2J&2j?Q zdxj>m)O>O!CE~A zkzpx802NpbS6S^Y9np)@!xZupH7`wg91Z+-UxchQn_0Jt{qvsN$kPk^Xr>e48_8!6 zx(G)lAc$xv|4e~pUQ_}_7S7l>d~Q)G&T{u_}l_fq0VN*%xX>%J~PX zc0P`|DFK|~Os41K>XBza=mO-)w!A%Pw>NM4*F9`k+b>86CV?FZwC5GOEWd&H-eb!f z0sD>@p&|UmLr`^A4WeAqW@!hN%ULZ5gKhw^6Mq_+4XbpF6|E z8lu~n{G)>dxYGlcpIL5127A?;Uw$1_gZw9*ix$x(Lp+bFv}@QL$dleUu)Ru?-wEFk zbSy3m?psQWNqpT^viqXQV+?x*bjmlHgXG`V-SjT`xyv~m83fF>TT&znVV!#>ZUJ|~jc7glj%_9H zYr5iZr3IqXlYm1@47cfzL^fqbfqX7%hnBf1-m2oz4Y6Z~rT7gAXB;Zh9je5-Mt7T) z%dV$6h!)f5B_+%|1#iyIs<9K^mdbF^6VC>%k?%<$NQ)SQWc`JQ!Ww{;%=0+CM>jO6 z_E~2g@0h<8zyEl&G~G;+x@H5#2{mW=5Q3Z5^6MQJCzG;6P6s5M>G?RP#no}9QT4{2 z8c`3@vvh2ckrSR?X;xR38zS=3{LEHM7w|8}3(%bgkH3K%VMh$L zLotFfYLPnEG)PZah|wtKqiA5hkC&Dj3<-L?#4(|hI0%%d5<*Jpn-o5t^-ylE~OLBK;@3lwDz4@Qvn`?$^wq4)e&)c4)$K8PN(ln z()ny!`1q;H8CO>p>9)UkZ67^D^UmSjtmo} zf*ICADyb@>O6kVV$lcvXYxvV{?DJTcP-+gO>vKREB5p?kzcVpNixn_I)^<+Wv zFpr2rz=J`7H`$@B?99H5xMc9yWpRGT_xVbh2cjxph2sdz8|Cry2ugBMRmRmQpLM`; zl-P!9m&LG$(Z!_n@WNfR6Sz=BN{oLMlVEr)@-P}b7vdFR#n0x54;Om(EnQt6YpCJ* z$Mlm^Z-F#s`P9D8G-nozmqKQC+)AF6_;GK;EF~>Ypo-qQgl}7v$!)TV#wTnvesmVz z_+;Ys#DzJ>BlbN>9;d@3cKw+`3UEc#lbfq`t1N2cbo9Kw3BO<4)K+bsW4m)232M00~)-a9r=Iy&*=d7zwr@R^8 zW3s%)y%La87$RhLmFi&-3(g=)8N)@oYoWK`cWd|}VS@?$;Hk@*U#Oqjg}}=o|1RO; zPapxhjEszPFF#;PRY?Gkez5TRkZYzNWeAqKP+w5(DnWipfTvz|Kg4pnB#HAcUhrpu zV|KRMbv1MO>OQzMv@eTiyqg83^7Z+Eb#0LBu$J#j`W5Pm%=~BL>AJ&@NK&M{Buy9u zVh{{(dZbMVZrX1oVGH8O0uR%;ZNFQ7_VFcjD>=2QoQtTOdstOLAwq%f8K=DRBMhB( zs05TAA@0GW#SXj4lg!PALv93IUxDv-4w62th@LaSibo+^8(crh( z`_Rd@IwWamNBQo5C4G}{sRY#p-i0-c*Xifm5F*-_7sCuUaOmk6RnyD7^M5z#yii_k zI6O2ogg4yu#ff*-ej&w1R_q}}Ma6c``RKAf2vlOza?q5@>Qp7_3S`KA9jJl8Y;x#H zD`?rPg2!BLk05^Wg>HQnHIHFi`NmK75N6}hl?A!tLv8Ew6ldf(;f7wlR)g=nB`U7_ z{J{RZTHKTQM!hV0fC~vU$#v#OUh^Rgt$<6YyA@B!{*|~oBUu_w5U%aK#?J{;Z>T~? zAg6ujooFo)*Lex?*;W^o)MXnq*6_@-KZ#)RxGPZjRXDb7UG9`@oOV<1Wib)8oF=v& zIV@@oUoPumefVOydMCAJufzEIcViWg@~!C%2j$NyP5Y0<2k)BCE%s{fDC&4mpPO`ab>s5FG0iSg z0|e#PZ0p!FSvbU6gQO{lR2U+EXsbS7uW`r43F*pu_G2v1zG&O7MrUY?45rzBelfw_ z5Qo>lLecVXiLb;S>n|zWZy+k0!_}?Aw5~3b+Z3)t?7$2})=qTh>>~I6-s*36lEc3x zQ`m=CgvyggM^}ccK*z8Z7QMHnS(C*Bx=f#JNUAJy1ulKSw1UMVf9sptgx~&QvWSe6 zy#ItXhDIB@)cpK|^#^1W$+({kYId4dgxXM$WvDo?D7bw^LGqxgjqod{=S&2uk*ANl ze5Q`3;pTStsEJ`1B+=+N&a0~?{I}P?Q)|wnmZ;@C?yneiv*tvVhAl3>3-71 z6XxdS1wKALnmfEsb7qgUkKhe+zwsf*n|r%klCfh>Tza@4QH;^@T<75VR=xFfYV#4o zz&?4-=m}q*LCnB;i2Bl($zSgUmbo0A^{E#MY6Xe#{j}rU)|u0kuDZ&R|L(-cssig- zw`G@00vvHbG_ZWFI<$OsWnk#KcYD6)jz2Jrn{2lzPM*j27li-gt)RX4d6$9vSr-BC zji+^`Us9^j>5qgh`T6@3AAz>=gtZkm5=~FK1=_eKBj0Sdf-&kiIkM(Qa)P8>LanrQ z+t-+O^h_;JgBGY2xT58_wg7fEW5cd~5wT+tkpwi_5Nl4WV-#8v){dWu>`7eMXp)Mf zkz|J6tf1a+|L}!Jjm?4ypvHb!@3Zfb@jyjiGJ2%}-N8Z}T+u8Fz*Yu956&UWkg$Ov zyGc`z^c4qQXyiw1U}aF*cw4h@rNGrG+6uzOv2;L5p?-iI(o++GM4^rTWD&&4Iiaoh z9%}1L`=~AQ%@YuvO-9|XgHVD*2zsI@SMq7@#Q9TBrn07+CJf%~^SYv-G1W;kQ@QFYE$-Xs$ z|K(bn>G@asz@GHWt#$13SAwQ|#})A=$0yi;?e-RP1~s+|iAq6t< z7d5Hudue+C`?z^=zV~b%hK9qM9;-tiXDw)roEe|4N5yskr_Jr3ZC;(#%~l<%nlr7$ zf7YJ4Z5Lg8U3pguyvno3XEfq(`aw6D+&+(CLZuQl;D>JXWGAQB8hp9y0W@|0#G7bw zz_qKo9dmVfRY=7aLm0QhrB#3zqjvmG_+F=rku__7YGpHoh4*6Ql`OB&CO|IgQGQOX zHD$8FmRXvzFw_&g#2;s4RJl^%X}$umGX}#oz-g?61T#%8nXvI-kd6uy2@tRn%2FRW zepz?MM7Vd_3mS(+y;i}E>v#fi|Lla1JGA18sP+*r!ImY0hRYojO8&X#A7itiwoE*i zX)n3vzyG8lMPA39p9_&#&Zt|zf8$L*z?|Ee_jz$q?|WYL1N7-l{Kgo7n;*YvDce1X z|GRMf$EdO0t;Gai{TrZu5F_ONDfv*^F{=VFO@!M)^lSbunfTpa-rikNUM1R3l?#yB z=2hBkJOxa8XmHE(121?voV$6^O!-68X~%g{@pZReCM=TfiQP7+v|+rKi(Z zBpq?KyEPxa*K8M^5dk047bfW6%p9DdR9B{*w*Mnw6k3 zidx1ahUV+QHO!IK=&y{;$lWWj0*3ErKbI8TvjFAuap%iF$g!u#$fS6u^Oy5@n=#%3 zb8GI(GPbBo_eDcJw4cKraYGnI8R08%+TB8zoertCLEE%;o7Fjo+M1eu&8%Dg4}?Y2 z_FrQ={q2R#&CMcgVR^3BlkTCdoLbjLpXr4?%s6D)nk{3BvhF_*3=+tFl;;3 zVA~NbF~?@_-@Y_gMousO(G5721uqqC({i=g@_he>eY3H^B7R{KW5Hq6FDCaZ2`BP2 zM00I7E(9mGTpxq8gj0P5douLf{HC`>BWySrQJ8MZq`4Sj5rpAuWDHGO3)U(0FjCkA zF}dcVrFmH%U4EZF-3oIah_HV-9ZN|}l*@MFD5>Y2u-zLX-3~dCq27YQqhp~&2V~?My zCxXj^j#Uwd^&~3_8bON|zwhCljz1_Xv_^LipN#`9t%U73##rQi z6ci7sA+*#)OtnNzE4Z#fp)Byp8d0>|w1NtoB2Ovd>~{F2)c)aD5y=<EkbRs76VOwDw%5Yp65?dXjv zUV;XD&=SGNpVOm`lx~0vL^G~jp4{~z7RI7YsFe?jG4k}6?oIwHn&D3vex*w7#2YAN zl#VJPoJmE3p%q`;sgp*3r-{)&)Rl}#+1Y%2x12b0_Mi=p)Z4HU*85MZfmKAY}7yz1-aT7_*SC9Lh_ssT9!+agQ zG^hO_YEGo4b|5YWA(zNErKfbi8UD@P`MYY0b{60Bo|HG!Tr<7kDMdqOPE8ID3)qaQm7jni^VX;fre_A{i)+*z zYfFBzIs%>D2PS`ZB3VKOf=I_GMC$2MSxiKR<6Bi?tXTrC8Dn@BTkY>bA)7Vto?F{THq23d4~D|1Y{M+4gsDn z8s{$hM+;^G#P?GjP};Z9e7!YIMIH{zSj`3W*&axEc zE}{8(vHBtYL-iIvD$#T}_6CO60T=BIss8FuO<@{0%0eoNH#1R|l%bP5FlmTl3`!xc zrSFiJW+hXZ`DPVg_V;}+jmNQTme%gQ-|8ni2T3T=o$6R@?hVC&S73>YvTt~>ik6x( zau#vNcJ9CS#%3Iq+vKr?u!gs4N7_wB$l4`YBADtYMnMbA4%8y79Pb0GQ>ad#ZuMu7 z!pko=*-D}o?S96-p?LFV#sGza(k^!}Y(Oa$`{F`|O-Z9F#?WKKET)=l7;#Nx-~gRd ztGniA&Hpi*P~xU&J>(N^R4_IkxuZ0%aVyPlb*$|^CtOy>TrTENtU(14JmCJfH?gcSuU4`fC{Uk1HU}y_2CN*nTgU7tJNT@BFsMVT%nYNFEPf2`~49ZXU zvT_RAcH{nx{q3>&gdz7pmS+Mwf(KvJnp-qChe{=4O-mte0&I&)9VHnl8QBvNj)d~M zqT}BW+@U{l5z~BeKaK=Y&3fM zFDWJwr8enl!URz_W<1i8?(XrfS_VJ1PH^AcO0PWlB{jz`Qa+|%DWUpgvyMmRMEEdo zrZjn|X=AUyUrNQoQB&hTl`0%YoZOa}ji1+i_3aAA*3z|35w%o}5-T==k>wE`&WewY zPqZeb#eznhxk&0X!}OQOu1OEsB%uB7U)8+1`vVsI^Jg{LMpR5~nuL9Cx>@MHZA&*W za=H_BW;z^g5(h8lerj_R=UtGN&28TN3u<`_%JQ@NIo-moY!3FH;JE-$T9G3*&BYkU zJLWV!7o3tHi}CQWA@)2faKdNyEU>D(eW&-*!a6gf`vSmRn>Ox!!EJq5Jh|zGL1$Y6 zt7nLTsc-BNk6k~4I0GO2iu9E^wzBJ#O6O7an5)6foF%W~qYK|=aEckEqyTNS#Gjll zy9ty~lg?*pvnWiIq>;uUUn|E{Paw@EJ-MB{ch~5{U1A+x5S4dotfO&ZR09#?5rmM1 zO8QGxCHYZPQx_IaLQ9rq)J|oonD8bDlEA$J-E_T;P}8wVMPZ!2NeIwxTGC38{1(;= zqoSs!X7z?dmNc@`Yh^*J1tkxgpBL*>VFmK+3VuR#535{gVxgUyQ?zY*y;skLE(}bs z!*r`62MW(n6r5pg8`smdGT7U*7GxD0672;9J%}5QjOAm_Qr+|D@1L5+&#@ygJTEB% z1n#49ub<~{K-Y4v6cWEy%;Y#)A{@o!V3SGX zt;0lbB2a2b9|g;Z72c!ijK1Iu_K(qW4P#JpCDzm-Q=xp%h-PseJIbDnjE|2`lpBkY zxhaov1@DZV0%ZhfCBWxg_^u1^tqKu45~Wxfogab0wEVZR6$DDXZcZ#5@U->YS@FN8 z$52h+@n!=RhZ}*;6CEBw-@^~8mT|aE2HV_hzX7S!$lyv z+KV_16&@4n`*@Z9YawJvNJx^#kwJu61IU6`v^A?bW#v-p`1WbV990B=?hAkaOEnTR z3ZtV;FgAFppjM$BM8%qKhK{omZiz&hk)Ny-@)PMxD|Q{qCch?Mf4rk!_MokD$x|** ze~?PzN)d$#DPmrxBg=O{MmIt_NYhOix)m+9=KXJX>tV}ABo*it4@ZU=pGX>Hdt84R z4_?3g!DYNCK>rHW*oT^6$dSI?up7VKz#_pdP1)12e)quusGil~P#aBRr^&6$a2KgLXA0-83ep zp?~y<$;p*@Nq;|8XrRhcQFEm1kE|xB-ch`_7QTOY<1!Mlu4TClbwnOaUKzb|yB^-1 zoIH@NAsU+fJ|bSTljak)Ut6rg1A@~a0{xe3f`sV=2%h)T8d`ol{xOD<0sG>xyY3B@VNJsxwbnl{c|)GF)PBSF>Yl;f8TsPSHFCmxFCyw+u0}_k_!Cn z@XM?umg*dvmFAj5FE!h=m_&i@2Fl)a5;cUQlI$YSr-lG2vfKh1b6Por_j$eOA7fyF z!+|Wv&Rim4dIR&F*k&mdZyJ!Nxu&X<`WMrgLDz$O5qep2bvI(Xgpgb}+F{LX>x`yk>TgvOuh z$6CZeUUJ5d+(p-03f(bU?%OKJ3E^$~MC)XiHwtgr*Nj0=@-YcbdYJ%6EG{_Mu#@kc z@Iir;6-Z$jKNvyseSewZMnU}+T>XkdENi?>wVf$9xGwKYK5|_-GHp~Vg&1>M zO#i6KpUpxe;baWN{^I$2LKn-QFaSWZn0{xy9wLn_g)D%(CK@NLW+963%#n+NpnUPa z^mLpL3=(`p`fUBBG(yXSGUWDo$; z5=zu39~9p*Zbt4NyMCF|&vIlDtAbQT?$hnzvAj1%6b*KzDJ)#@}Gk5=R-Je?Bxzs8@m?oU7{@7g^({U8diWGb)+6FZyfu&Mye)%l_&b9$cl`+AX z4dwxboaTJ2TvwtRSMWM$Z~D-Sl$yJ^3W@s_&_=BNC1-qwMg%Dla zy*7=&kAtBK&gXZbZ0mkyKXI!YV$-H_CJO$1>_!QZhP!iBV3zOe4u4XtTK{sgd^yh% z>&u$$tlx(;%Ve(h2R47=J>dn#7ZY{sFtTD+)VczY!@tIUz6jT0E1e(nQ)oeeA*i%+|1j` z=*xGgW~lmA4&M*z1uZ?Xe#o_~JHL`FK@G|Qtz?ahnbRZ-qR;ny()p9evT@P=CIw9{ zcam@iqQgZfHl=P;x?00>l~#s5p6}ZcamcFgOXI{>D>$wK9)-gd_aMvyS1>%^Jn53T zS~6ymB+<}2bP3g7unQyR7rn8@1?G_k)!DaRs~=PQ-O58Isvdvo#}Uiav{UAD5(6nn zph^RbTF^ty??N27(GqM`C1?si-xKpEnGC&CLqe6Z#WXfDh|W1|7nRgKo*%tB8`+l8 zjixE;i5nS_hlGR>`L0>p%wqLst(y))`frEjc{&`=Pa1vnT5DQ$*z59zL7Ts4+fI^p z%3rW{TvBwdkO;(>ZM?@su;>1QqSbk2>&n%&v^~Mg$Ur)FOiaupj=D?)4zYSa(%wgsC^aUjiowWl~&N2xdZ|ojVS+&F03nZ@C z&iWs4a9B7_mj-E6Rds$}J{)Ww;uQVxzZNcb(iO{1d%Cteykqd!@u$0d5G)9eSRDxV z%Esw!FOuH0{2K%GBiUM`<}g(n3rD|i#_P*o3zX*FH8$9PrB9b{jK6VXZbm>uxx?(-6b-r}?wpicj@HKX8HT&zv~&Y-7bzND`UA^mxh#7xb$I@F zD|j8DY?gC<46m#(-I;NuM%ap%12XK!Z&xP=T5G=WpA<~;7xalvGy86CxFmy;TNGF2 z1`}SC&~2)@aN+Pi$f|U%5j3I$#&X|+2AYV2YK>6)P+J4GK3?NftTnE; zl7hJp{u;H{Qp1Cuj>h~Qrv-E#Z!B(}9uS~+ViQ}|R-$FoyRP9<{q8wVsxwEd6ssUP zofqQYJX!j3xWZ=2&7GDDqbh5e^O%SfTK&Da`&XFPhH19(QY-aFNu<)(3%L4 zv!7^kS}N$hb+7rO)b)Gv0;D{vg1$H3!|8i$Dk3*Onsn;l!uLV_Sx_Cxzqow8`U2te z_LmjxWdY0VcQ0U_<{NY(RM&hv@&F&w_Z?NT-B(D|2ZGS^@iHm>+tJ+`r;ar?O&Jl` z(uMz#9-BsF{xI29d4XKlN)Vqz7eCAL5SxrnPmb7;qHF0OIyoW~a5ey&WGvgAac*8@ z`?mDJ;lAG5(RNCb8M>06H)M=lcPpJ}6JA5>e#cFcec+xJxi=;Df_p)kqz$EkI%(-e zZ}4hIK^iek)y>Qsh5N^k+>v!$rcH_kqGG)dp>EQc_J)SnlhLV`%_rH6MP^+8Eh&YU zo3npHcibBbXxrh=n?=?Buri((OH8_6M;z-uCg1Y@Y(BMsy{~^ak1nK`6k3mX4u+W4uLMxf&fc zh5dPOiuMDfr%8u8Zie`epybSo7W*Upj9)z2^WW&%^Y?BA1FPSMX45 zQ8!~i=W2~9WYoeWwKJE^a`-{N7MwwyDEM!gygI17aNNO(!t@I^hM=ZDWd$vC11}&S zgHb>}5z?Ih50dm+P+3c0CT$-wTJ;$USY(F-4|EzEgtyvw0G4_e2ptzNPi!_~a+J6hGt{sxDjD^%RL zK~xvqKL`%BA9R~rn35`*nsU|KBCx~`0)H{NY7U~EA0NA9OE=yb{k8l!4)h-R8U-GC zuzMQXx!kEF&!-uUynRvmV~om~%#}Mt_JTfYjfV}Esx?1VCTmn8)ipP#>x){jDnfvC zR>L&MuGouFP9==0^|5ep*l!e+QnV9WZ1RWS`xMJHSRCap7Hebq1HKV*Y10^g?(_cD z`kEOadbkspQ(mF(&1$ZMYrQ_6?rBtspeZk(^hUWTcPdyLHERCJLQB4N?5An;gPW>0V<%5CLl=~?gibl1Ui;l(feG(RgibmAqXm04?E0|8H7 zE`{7-Pv-B;5C+tco2h7A%KG}}>FISXW8Evn zJuGK(n4i*7G|*Ak8>;<%OGVK4N{gv^51lQr_2Oe<$=&?2XEb-h*5++E;S=8v9K-Mh zM{*mZo(FXn3_B?Eo4NVO^lLlseN!i$#gFZt%Zb5C)?C}QTb|(O`{?$C+x_`VK^>y+ zv&T&}R@T9H)*oMWh|YtB`rvM<`pi;avKq`B&C5jTQ$U+E)adp8eLCD4M=NP9$SU7K z#4D8>y&EebTyaal}6a{=9sSB z&Btzm=-KMYok1j0Lvc^cO!cAm5qiW5@#I#cq+pTuEWDk;KyV!*4`s}xL@@Qb!kUTX&FyQ9W#*vbP452U zi_!hBROp@xVM&L^XMEh%8ysxm7{ezvy9=R6E9Q!Z(OXz9{%c6!&F5?T7VlBUT|Fl@ z(KpC@?)9maYTxeAXR@BNa*x|p9m9=VZCrtFcYEJa;7x7W13JAHIHzNQq-K*r7yX85jC#%6nLp!%&D#<1?O=yC zZdh;hGe_O3Th8999uvFlg;JnH-v_kKIhlQZSp~*rL2t3V80&P#B3;PeSq!i9yR(QI z4XXbMwR;l{h*#-6o?dUT{VU_w>WkYsXD@ahmU5n;S-axaP{#Fn?$RFJk9hd3heV+PbS|$M1+EO2Q$;y9t(j+s^{M2uxf@4BaXuHozlI|WB9ORkarVFS} z#xq((Z5Q^RdA2YPb7*Nm0uU(!2>H;N!I-UakOSU-ru38WHthY`R`DyVcCrV6X?6hg zWqyi(0?HOz&P|7o1&@F{r>>UE5tHor(}LOs!7SUt`kv(p|G66z#^%(Ob?;OpyB6}E zhSl=FaLb>~U4&R{7o2LWVzY9*iFK=4-9olc;zW7Xb7Lhfm*-q_7>Lwd=ay^*yo zlPAW)HL_DB+dx~YK^0^0bccI}b;hZ#ZxfR4*v2_EKovJr3#ADTmL!?%p+qYwF3r5~ z*B)J-D1ZE8EMWFcr}Vd89LYS3i;^y^U3FN{cZ5%#1GncYy#d=j$VY-;8+!%qp1xT1 z_eMww91CCVoL`^gwmWC8@yf3rSJrn936HI2^kAU=;BE$(kQBeB|8`7PX3M(1UShOq zWA zEz)VB{4H$SDP{cur`Q3$uEbT^__cGbWd?B>x3_eyVOb@cTjD|C|6UIknp+4S!>!Zv zzY;sAXFP~~_Hs#!3n)COPI&=L`@8i{xx2*z9Xh0$6+t+!t}U9H;t0e%pvu;#hxg-d@vHbWjoy18@4W!t$n zV))vXNrJKPnIy-ZYx7`}{U+o3#hd4-r#j1(QIXF9QIRF9g>vYo`{`w?{3TG*zfCAA_BoMe3U9U6+VgChU*Lw&kG+aU2j@M^ zp|7Dm1nJx}3b%?M^nFd{oPagtL;nMVeo`oC_McAkRsx#GE%@u>N$O?V z*QR*)ku2mpxZeWnQz-wrx;}qVM~}iiZNWv}h3n+^TSN~3D(w?(g)G+WM6QpK#~@oE zTFsbfPF=XRdbfZ2K_kWKR!W&5$J41xjSL!bg?43dwharA9q;dO43V+(o&lMQZHVdX#oRB5rc@tA;zEz;UFEgA>d8jeo z@aFb#uxQIKe=~D7&V_uTF%#t{)jnbQ|LXTP-U})jLjIkLSJ=NEpt~poL9+=g$dqhT zEYayxXP);4dJ8`~7hJQIXvM_EKOJPdlc=bi71e$-xPBi(Dz#BL13jkA)(d`(f3Y@0Q52XSd2s#|9w3 zakhbH8|2buSq2+y)F40Wjj7Y(?Pya^M!7SdUC;CBj>QUUoa}$Z)dqDeStu#|YdHOu zjnuW;8Hdek$j_IKpq`WlZCQ~d9MxQ$7l@0@%JzO)FR35= zbO&+|189CzbO7jrhC8Tb{DhH+uX0wp9`>4zeWP#jQH7%V&)jM7hV*8K(4s#YVaDo& z#6>}pV$FtPZBmWxcYNY9>-X20B!vGO18~kJtBP9NAdNICen5%J(4EQLAgvH*7)nXM z{e(i$>M8ogD^0vA-|}DYMd`hfXx2AdkO+CB-I@#w9u+-()OWIgg_D7uD=|82pw|lz zdFJom??C5}U~XJN0mbThHhn5gVDi6n@+{1lm>5$Fi=>Qk3*|Bf8H{%-2 zsMA>E^Tg)&?#ZsDrFDC@f~%(H&Mg(s5}$bNQ_G{Qrzby^Bd}y$-Ep|y!Bu4SWzel~HFf5}Q7q*Np8&jvF;?%nTKo z5=OHJgbo#A=^e=){v95M!YD6YI2d-i+CZpNza}2&c}oj~hGR1wj#qHKZ2Z^qWK(FM zFRnsNN1j(8yj2(DM}3Fl)@lfVR`7jf##+mKCajdb@ZZDxg^Q8<3^%(2Z_jjkxsY(L z-(N1+9X7wiq7Z-zXst;2@Rg*bT{X#+0Y*znt{-BzRY^9`R5t)`ly?Fpu1uMch%bdc z!uH(}LIeWJ2p-ueT`SDUP?K^T0n%hJw%B6oy#4yv+Ge#m0RfXbK07-bA~@FaT2p^N zNh4mhWTsYS1CWN>+>|vxO=MJ?I5%YEo~}eYw(}U&SZQ>s7(8VtCMV_iG{YzF7<_r) zd)uFv3IDs>)y_~-Qc|6$9FRr)W0to)(>_-s`(r*qus@kCtE@~;ljFhf+0t^@8e2g7 zI})l)Pe+wq30({dUfgk>id_?ZaVazqJrn`yr6GT|C=Y3f#2dM~87xJ?H3Fb28JOv9vT>tLktBX_tok4=i`(1kN20;j)(Qz^R+h7sU#rx;SFA1 zZg6R;VhIXC!mYJgVsmnK=J~nrQVRu%Y^3=oI7mqq-7VF~f_VBjt>A|8c5Uu(uymzO zKddBK{78NnNiU&&`@n*MKR^sb=?g=%=XxNY8~KxRVY?emDLF>3Ubc3(s}p4@=Oph& zRIlPJ(lb7a$z#-(I^E@lKFv>KOnQhtJihx^wB%}#(EjFb!Qh9U-o)irAAoy&F)L-% zm36nKn{W5L(QNpM4Vd0(*9HvG9ypFjBPJ)3FGl?(k%#94msY(C9v$&FZ63VR_yZ#A zNO*%^@an3~JqcInlY#Fm1 zsVc?L(je2&ggc4A2ZO<2JO#k7#krM&(Rh29i&>I7o#=tH~Z2s+NP7e%z9x_K( zA5h0qQpH3qBS4`lI11gd4t-M>SDT1aC1+9nBn}kP`(VVo{CuB`)?h*Cua|vj&ALpX zD1_A21Yb)P$Y^LH+gz=4Fx4;f%4A@CM3lEbBYe4b9cabYg(x#3Qyqw<|IDqdG85){ zBCk-P(*)RI%1jTy&Hcx$2dG;ywp0Zmdb6?Agp?Gsb@OkgWMu^u=n3^A%2-%xtSds5 zI<2Q3P&Mz=i%juvI9rcm1{X>)Oct;PF-&S2kZeOL7mF&kI38_Tkg*sKVE+hr zcPI(9Ku70vxkk158Wym}obXg-Wo7An?o2@c9t;#DR3HT4&&2Z$`^cEWYgVYRC1I#Q z-)Sk%t6~fe zLm}tD;VlN6*V_ZM;^;EajNc(dVTSGn(a~aI10*)9WxElCtRfYaHd``)MxX0*ji6u6 z#WwD$u8tEpjy$HoaWSKWjh$AOIbV45ZoiV^g33qla9dPg8`F&XBebEt!%odLpH2b> z9e8e2bMu1#nk+F;!?cF8l0(QiSo#f)w%J=DZok?ktzL$KYxw% z=OzeCNJxM%GmFK!|Ene`1oqdOjs32wqUPe_y51p%hp8Cvt!OSA3prL%#`luyHwa`Mh+U7#W)^aV|X-fO7tXH7^odu7w26E!MSOJ-x8dI=JkW}=PYkmsX7 z-34D1nsw@K?AOG}w+zM0ZK>+${eaJA7VYTWlG4Rz@ zb2BoeB_v=4bI}GO-}TDITqua-@j0Qzb{iSDHbYR*zfhlq2arQzsbA)&lc~5stIa8? zUOxFezWMcTvE(a-1MQNdDfGaJ>0XES_7gXYMRjnIFRkYCnD}W64daz0=eaXtHq>(u zn{LiXrlj~3p2mU0RpO!*-BB7YVxmBr2P4~ph8WnfvO_>grw*cOfF|FqhZn65FV`sW zq2=4_Rpn}k8bMKtlSd&vS=Dd8XYl@WP_T@WpxLfV2vcm5& zq%>J^ylD_X7ZrNF6r~X?BXIt3qdf0f%X2_Xa(;Ha95I;fcO&8TqZ7~Jdy;k%E~|t4 zoLT(GX>T;$QViDw3?KY_#^b*RDG5>$dmci6ML#^!mg!#fUu|B|5_#X0Cy!aHmDRniHjvX8^J*3EiV;zQ0QR~ED<~luQ@=V5g^=?X@-<(( zv&oK1+pSvW|KkF@-;CyfIr8bfiJbXzBVI~BD_#$kpBqXaec+8Ww0Aa3=m)3}GXeF~ zKXz-pE_!id*HGZ6p$TGCYxu(qfVk zp3QoD3p83KF5PBE929>|Y2@-K3O|oIDyH{)f;@yaV-}ZvaYb2l?J-@D*~xFZmyC(5 zrb0**9h|GyfKwyf+LlHDrMj5jOD*KN-G90+OF35kX{`_raD1fYh`>r}NtkW{m{SNq zCx5M(Y#32fu23hocuVA->;B7S>Ow{Z(fEo`=7_}ED4IvMGkyYEmS9`Zll2tY)N_@} zzc>Us7}3afh$7yY#K!tcy*5tj#JFmZT@#!K;OLf7hR-CvK)q4fe3@4lW zj-zKELpx}4o7rEuoD-kn1tj%$H2q+jpd8Q*=KO{q-%C_(EOwF@J@H;=E{+MK&+@g>4=g!-4`RipnV9wRZ10Nv8kJ;GT9q zF5>rdLET{%ihq^_h~#~-1zVFLMAJj<^wuL;P0quV1_^MgRp22(Xs6QUSIU$a6rY#t znAekl6)kpN9YD<45(u%tj00o9MM#GUk?l9OvX ze5cw^W!6KVkA~rHUb9<0r{x$O8_PP&`rFvJ3!CHrs->=81kWy^PrQ`-$(^+K%wS&g zLdetyhvW5;XR7m&IA+pvY6m*#`e({9$1w*u0Az-;7QO`%7;r8Cu3+wji6uqEMxW`XcF%&}LE+39v`=O~rDTDVk!ND2t@ zvBi}su$i^Y>fw^C5Rc5;O1eQXSH$!tAInuCCJbf6FX=-+(O)i^$h%o!GVNm9s>`Nh z)0ooe^P|hkRFsvcStU6rvuriML%*^N3}p*ZwZD)GUP%gKYB*hz{Am!vVkr4a^x&%a zOK z4%qZP@11kJuAmUle3xni+HUDHc>fY&dVufhJ;!?+#tt3dIAWh(WIQosT-U!F1JfxG zu$ek3c*8nTwc@*ds?=?~i|cwN@r;C;k!A;7qKlY`{7v)_{&E-z;QxK?tkVS+a=nm4 zM^Ddp7?0y{zWl&_zJqy`a3pbs#d5Zt)82GX{XD?9WId`T#~9Hu@Z-pjVWZ2D(4YS& z3yC;+-HARw%a&E@no~f#HeeXbnV2G{cr-2oshA2(i)OA)1wa^Kf|}Qu*%sHHD^SvX zbRgL0cCyou%P)HWg`E}d+Rkv*k>=(K4Mo|V%^JpHO(+s=T@Pmmm{j0e_8s_dx_vm_ zrDP=<4ro0tBF@4T8nGXti{3Nh+p-DVNG$BgdQGH{n7++%vne%oqz5mVnXxBx;H3j} z5*?!A+WO>~85x59BqBC4cB7U~IPh??U__xBYL=e>g|4WT)z_jFS5+Y|y0tyu9hGdU zHcT4uUbH(qUnnMl{RRg}Qqe2MUhqAkyo)=_`Mm+ka&vabU?j6iTumCBYGIWZs;iprln?3k)i+j#3y4hp^_2qf* z=K*)VA)dLI;k-^3ri=_*mVuJxs<6!F7^3QRoLM zPjlaVIFFDecyhn0H-*Z6Fg?Bzo2h;a9T`57@Jp5b&&}|~1L41^scFC9pQ)?ciAThh zRngkM#&MvSlyo1Ah6A3!kPyJ(vJj ztbRPg6jF>$p`Q;YI|k_HxKdaEpMyaKx!N;~e=lpZ>wi%0UQ34M*e9HH1@|2kBsCO6hP@MOuGsc ze_l>nIkQuHF?B`{|5#lzU-E%t#6)kcq6A6NTD!Tutnw}i$XauG$V$870K$DV4^Dml z26W!3Tu}?gJEW>DxB^W-%eUOp3o2!GK=wHXf-JqhxuJBB;w*a3x7TAnyQL`Duf^o^ z2#3pf>#T$%|8i$Nf8HDM2#~z&b)0+M7~4$4-A>@?zog*#WSuYm>g?f^_Q52Iuv=^W z8-LYk z#)}`)0ltZiPbJ$%COT!~-9RFzrKOFx(2yxZfsTmX7c0vHBFw9H2d4r6gSs}Akv=>A z-g|O4Q>nxCzX+Tb3jV;l?K5YIi~X7nqv|O&3Qac{Qk_x?|d0B*M^_&fTG5m zbftm;h|g6$+Xc$P2?zOifKnGqHVJBJ(*W1u*| zILv{kjAbrTO&y8@;`@bsf{Vp4Yd<0wl4& z%Vah>eMiQ|Ugq>{R+{WBW{c%HI5|~RRY40UYk$ZJR{Q)@KmA_NHTB4kBRyc>^lPcT zS62Scu9}+qqG_;abU(}+iqq)NGX0t6w8Rcz)S8a{T3OAA##B(J+WBvS%N}z;vZ~If z&m?hS`J!R9#si1|7ziXwxl@uXc+n8Oe!5KyC-M;A%NWS7cnB%X^?kxhO&z3QD-Buj zJoQvmg38LWe;(?*cXe>ZCGF5%+~qo#wUt;TNxXDwCVtV0vi0m6MAB#VFmpz3VMzPl z4NG$gi3z{@N`{`eSrgigmiXO?yr+i>odt&3&}0W?CBOzrXOJxwmqC_&Mv8q_M=jg= zoV3BkRbI_~io!~wY)7%J zGrqgubB;w6G7nD2dl|2p4~8QZJi_rsT{*-{d7^jD88?+k0X7Yk1jmlpvg^(s40>b; zmAV*@k=A_jw%ES5dDWh2Z19nGF*Q7w&0=?>?Fs>eGA-6`RXP-%E7*_6iGt`~+rR&) zTESs5NOOxRLI7|>PU6S!B9(}}vARlsNE~2}R^5~!pB?$1y#c5Jie_F4l2_EAusg&y zsWh>z4`%zG)a(#ZH>nA3@*=9Vv4(*NDx>L&8ny_^E5ky38ud!;Z+ig9yTInmNb_Zp z8wS9^<7siC0Q}6+W~nwrB>%6Wn#2}5cqSFZ8AX(+ty@xh3xNztR5nMKBJ-uaN~N?0 zNLvs?dXt?gyI0(J%F!PNf%x2WJA!8L?NLvZ1dj+i&Ia{jYT|JEVgaHp6m5;relP-< z{NhAmD{bjBNrMpj$Du%_RE|%TOl$&AoyH+Ut63|QRt0h&nd%4aD$AZAXSwm z0h%?TK3Af?+OLk}^Yj4{B0YTyBJcsH7P%r71ZFrxVBc={BESfg8J9nbJr4NwdnX+jahhe(Mm*=b=d{hk@Ma*qcji3awY7DLM>OS|<`NR@^10 zHZ}C1Pu_rAUJ!nUPL}@v&_PU?ALpS2! z)hj?K^jkS?TTxS94?Omb<-+r^EamvH%E)7hPWU9j^~s#m>EgKgQDOA zzxkO7V=_I?0MY_%^|*%;#HaWB=kU|E`2gKxH?_w(*JZweBIg(5i`lMIg}~wnX>fRK z0OoX6;**cxzw5tL3RKvpuma>zGzHp~q35FAVYD=KDTp*+2dIvYjvt(h!@6VR4dAM( zBAWw+|3E$fp+-hSW9R1|A0C$S0F-~r^U__(Rku{tfrw^RlfISRkfXpsj|)cR)-$LJ z8c)oCnVZ=6b~^0I>yl0Ab>^Fyd1`oL5u!$2uW?IR8hsK?kSLDuVqhgASb9w(UXJgM^zxASvU* zC!ME~!;PLjXT=_ox zxBdL-^J4`#%9w_DFgAoaCIMwz-WR*QXs}*T7IvOYn$@cAthea)8mC+US4&*xNgIH{ zh;-2be}TyT^Tt!*9RKUw827>P((3Kl0k_E#I#;_tbe8$bZ5G=`uC z#uLAR=MF-JuRB~@m)Af(_UCuB!$1<}R>KF(ppxw*2Q>ck?$80l4@6z)r;N)Y_8w0R z{f{JQXMW|IFV7FE7=RsxBL#9Oz{rY+pre#ddk|2rXR~R8h~1f>)rnmHDFcZ2c}-*e z|GY68P!L!NO-BcN9hd%Bd;u<&V>|)^0#}j_0fKC?3od?sB8B@$-$aE#4|tL$zzcKQ zE_39&4$hh_!p>LQsKZYWsBN$$zn`5|WX_FM%#~cv$K724Sp>8p>E%FIOYFqhLG5F@ z>{H#8y|Ltnpo9OIQ2U9Spy$Ox%WZ$rcxF?MyYHL$dD{uaZ?7i32O&q1Db)3QObb{!sCwD^ls=OmL^_D4Qn>)>hdBJ2jv^7`H=e$Lvq0$i{935fC?dE^R zRpYAoCR<|5{Bi3fuI$@PlCL}kO`p=@kpHjWC(&MD<5HIbdu&IHgL#r(&fBVu;-d1Z z$Teki0I(79yqOXsMs+(@?6##qwy7W_g(lnO^H?gQb89?Z=95}DmdYNtp;C@woxYB{9FDwsoLgGHF_{4Gz(T4JRGa&ou(ntSh z|7sy{8w-+B-b$i9$7P0n`Li2gvPCR83B?ZK@527NMz~dNfRwfYFPe1j>pesjBqa*4 zT21(kxBD>_O$Vd#SN%WUO2P20yRfdGl_e=Ur8VO3!&?c?9E^`0~G9U(>UMS{I4Ef{WpX%r(Oq$*6&A8Jl+&@_Ja?q zso-8uKj5p%8$FJVab-WbxE&A{WZ~uz8}7f_dBOPX_aI*Im2zt@68)4E`IaG^#6dzD zV6s4<(5dBMzx&+#c`-T6Kwzu%9So{O>znUzIAgpd#qANw{Z!q$^Zi|WVWWD7s5vct zShK_HoAds-^q!ci6xCfefb2%&{FHc@klN|jvdDJT#5IgtXlNhR$T{bqYIwbo%n5cb zs4|1R)E#T$YEBi^^)hCLxOje!0zVEhi|LNpJ53&EsIKy~-&_a{g>m^NND4WNzqma> zx-dpW+V%aaev58Rl@65p*RWbM(zWi+h=wPM2}SQ+_D`x`(!hWe_rKDy&^vdW;Ik!n zei8)6cZyalhLMDD34-Z!m(5BwgF18h$3;quUG9$#uDYwF>dp3t$^939SNy2R%a^du zzM_(@umJ-Yd-V-XF32_4{_Y>4=fegL_s%Yw!ytR^_AgI0zk4*<=F6#f z(82g!`;G}a&jF+V*x1D*`p~#j^MSSf9=M$A8vL}FA5xt4*;cML{qaO+bDIlE@B`=Z zAKGRpB=-g0;~{;I>gELrMjKbRTHvIT!;5X{f!Z{>+YZ>M%wEC7CtVa%Rw%+3E$@lg9nR8k~ z9*A5q#=4Zc-ZQ5*MmF?zMphMl4k(HIdz4>E^KK%$HthwS=!6grNxVCMe_YO>@sBk< z7z|p`2ZrFqQb9%jRhAhP}4rK`cckG>0q ztDQjbmijT?-?xag>J7(6InX7zy_9rz!7daNr~6Yis7VOLfJyQCf&5;-q&6Opo@QHo zsmGIyB2k4a8%R_-~LE1@5rg z+|Cfa&m4Ctz?FLs2Ld(v_Zo)8?K{x5^9CZ6WC8cW0o=~IT3Zam| z;R-?S&StFk-}vKhzBK+O=*d?ia65eJUVb~N-O`3f?t+ik?M~JEbD$%Gc(0j`eB6bD zq$C^=e~1i6oUJs~c|V-9+~K=YZFf)=K!%;PT2^Lo*^|&~RGE$>ej(S0mHYp>%?w3z zEu3=oO5?dQHSXBHD!sPDiwg?}2NvA};tE`}?&+nak!XDO!;=$}^VOE|Y(6d|s$FEb=f zDuWiAxdhSCHn||%LhXOAt6BkU4O^m?*zQM3cXuv6x6@$|OpVD9YHQoj$H_zOw@CjD zyXkMi)8eXO&It0RQJZlAY zh5n20*4sTLiD+M5UObOWOC0pl2BL7|UOcN7b+-ab+Lm}}509_G5tKV&gK)vHXCe(y zCpV!QwpFHGy@QuU!c(B0C-wE{ z^wjkJbm>2ZGonBG6Fa-`?{RDlYr`|ITd~Q#*7^|s37xWYFDS#|=@?NX=cn^?CUhDE zHW3e{Gh@l*e_tZED7vY=k4ghkPfvmRi3NlXoC|-~)+Vm^#t5%F!knwP9xH;2x`ZEk zBVy}fU3qTs;1*0ptV6FR_gdyV=)M+g z3r;^{fxs8!7_K`8O-)rsDf)WO)m2>YPujt5@po-SmNl1B<5WT_!chQ^G19cS1|*51rLd2Y~H<6B^@ybo@>fp|Nec@i?bEmEEbUvo_ z$7}7kG?BrmS+ua#1iPYOPq6zN2sg(su_a2Q_B@$AK4iO@GLrI-yd7sP>zrhF#QVmD zVaQa(UimyHN4cyqh&8W%JA=l5EZBW*5Q&dpp+evYxgvJk=;p~(LFZmep^5dYh_UGg z$@%kE%&DgzTw2$a?d#Uc2LAn+hTD?8Dii6UVgE425;hI@<3uIs4l zad~KCsPw>~7vVq;N?m>n1fB^?ASH$qRQ(Jd z@=&f5-ASBK1fSir;`<)v#m?>xlhKW5T0s3E16l2Vw=gBY>b+>ZGB9Y#0hc+g8TiLy zmoHGP(rM34Q;HL#7LzZ-ufk7N-73IVq7du|_u@sfwVxKqYI@#kzwbSF?M@r44iwpU zq-I2^NAn#IQ^W!~BbQ_@f)?j&{8UybssK-kPO%BP(x`emxcV15cp#4MU|E0Pw)t~n zsk%8m?(~C>Zq?ywbv9 z){AT$!)t?aLjYrd&T@f2KSZdrPuXU^Qi4DCk_fj_!nc7OMh(+?zM6m$qi~q_vq|4! zX1?B3`@aeMd4HT0x_!3A#lrs%tUh>0>~*^_Yj6^QZ!)5GDk5!o(2zK|5;gK}`l!!234;dzZ z3*LHT-)L$4UFVgho|Cl5Y%Hdfgm98#9D5*^UCJsoo|JlkKD8WZ7qkvf?$Q#H?uqA?> z?0eti{vQ_rI~DfGOL&AeOL1%D#a{gM^dOMWj(THyAR4{12kXBP7xENpw8WJRXXO zwUc(>eI!$nNezdlMBV$ZA79t*q3bbL-*zroo!6n%vz^psArl}e375DXfU{gv#V{iP zlY}@3C8Z!pW~IsTq{}yfM|=QWVD5&ITA>@F`sEt_dN5wp&1c#T!w`j%OfXqS4&+=hi|JseG88Uvg&Lm(=+!4 z707tNru^c?hyZJ{W@4%!fu;~(To>53#o$e;P>zd)1Qg&wS3fC1^Er7+IlpV7DK5Mq zBUA{6zA@;wjvv4@k8YNLjH)thqhftPPU5G+=%Mgn{tCjqs>%FUN-Og(uX zjPWUnuNXEsn>rZF!J)TlzA}~57#bq9F)WDrC@No_WD+#;>JfOh9>F!f3hnp4q@S$J zs?31RW+95m_5FaAY0EX1ZZcO7%ddtucNf-I8;0hA>6My_i0`B%hY8}PDrmsSr^GN8Fk#;mMSzI1+J6LSbwzIda1P%1|bYSPE@Ba0tvOjfl$Q(^VC(k~cr>U;MdGX0K`WIu3{wCsL7dY9Zot zKQ{$}`=usw!lm#SnnoLhG`lDqp|S}cWojA;XOU+xJU)5zlyjL& zMYb)39?=ks`=f&jnjqL;e<-3>Y-JCxFTsypZ^hqCAqY?BY4v71oTx3v{JkWH#4BVO z)e&M!hTVCl5TQ&FZbmh>jAugfx>izF`f4dtm8VK~bvb3X-yda=hNlISH+*oz|FoNw z5f3QS=Rr~T)xa-eI6y|bAb5Q34du$J#u z<&JoVw|rd=@-oB9P7opz3%{mGdh7yXe;r-;)%st~6l3!%uGu={&`@2bkKgAY?r?(8 z94n9OBD?kG?+=K38_DKxpdX^V-+t+c^Uht`Z>jb)_EbHU&@HNoF*fU7DXRTe%TpJJ z@n7NWtu$)+2GSAdMxAj)k3G|Kv)$c9z0h=8XLUWuqp?4nBy4AnvlBiK*DIDaP0vQRZJJ%s2?YVj>1j4<_in z%2k}}Zom-a1ML{Bw*{XQxoG`O39D+)FEKnE?MrR*KNgS_u@(E*FfIWI$g5v(OiXfH zL+Asc7eBv5@vHSOfLYAD+jbvG+O~9+>{A?+Zewj;4){kihIOKsxsPJ3OOg z1w&BH)Jw}eN1hU_zTR@=531r%);n~|PY$jE=krzL)5To2F{0G~BcCO|fg{eR*XQO2 z`p|^NH~&vb*BRAB*My~qYC`CQ7QoOuNKu-h2?j$4=|w`5CRIe~1nDA86bK!pNpI3Y z2rYC$K$_G@QIsZp@%{Fk{k7-p-Pt>LX6`f3o%zP?Hdp0@!)TcQN=e|AYYnu=GI6&5 z_2+Cp8{CLe9i9z^>mq3G#9DeijR%LZgQnF^ofl**Z!Ncrq3%az`~VIF$}*t}$;%it zT}1GhH|ouF#n;HVplup?JGBg_?J<|=VcyFp*a(QiuU4Qcl`I*0cf*a=!el)pqgxhb-l6Ib) znUSshlv%Y$XbUT`8DM`o+w`fwW$Qf_qwy+x*|VlI*Q)c%Z_Oh-@=2UdM(>P%!2!&^ z2|e%Ey{@s*cfU$X!7Sm0C zspU>zedIUWDN1`WnR$1d+e$+%I1%2j(m0lLG%mZy*_u>FuGUoWpEH7V0_N zU>fK31V_@&+o*Lx`~#t0?K{)=wUp zGy}pa2&S8un(8HBf$XH3hnKj$aBM>O5Z~2ORWj;?m;I|)QDJqp=YqV5g_}QqLBqyo zrCxsd{d>;GquDduM(g*CD`*}`oyEvkw%ISNP=-Sv=)wC*K!g+$m^S=gY-?UDWn%pw z!oLY5ifO8U{rNJfInC}lynx*f8V|f0>&9UPqHt75j_9yT>n1Bo4h4bJ!A;3~hV?#o znx4PSP#g;QzmX%F^GQpd|8!!yQB*u7_0;U)NRY}bhr(x_0}9LgoxGb>Hatu@FF!Mb zdgAa!IDuO|#JU`9Hzi{uRnkw0^(x0z3MV+2!m8X4FlPRP`TZ(DrKLi|14WqT&a-J%Kn4!wi(a~lEu5G&0gER3R z!nQiuU=oLu6uo7voa-6%;mne*BH_E{C_SKHq2so<@PaoVF*9_i3~tX)X#h+n0`OOj`J12FnyfyYtNmRZz;K16oVWHj@t~6Ez4DTR7--;PiqYV&# zzW2|Ly%j~)b9cct+;{!B^bkBZjHAN_Q=V7w%z?ZaA}J{Ejd%lBQ^!$Z{sqaX=I)9t zFSpVTrQ|29x&X^dE6pP3d?pcnqYcq}YJpoy0eAY>Xy3klG6J?bW@Kg$tBGZ@)zHxK zQrIPi!#aV`NUb{^^f8*hC<<8lCVylwZQi7zsqtFv5$q@wecAie!hdc|N{y$WB4gm{ zvWyrib*AIRNgoCxyH5?`!6ds#Ev!cBeTy7oWP3+p1CK?~r0_|kvVeY99a~X1kw42l zic^e&12!4Mb&^0FCfv7Z$OHwnd&f9B|IOU3(FU6DA)cP%bp)0CSmktiA?LZ=E_j!M zaP92wW}65umilJ>{?ht}sy997ch#l<&(T_)KC&kMPcKA?j98tt(jy9vVy`=JarCC! zXrug1$*lInP%ExHb&`$N_RybZX21*zZY8&DwDJi1+G zKWMe3xQFK|i5;(mbGHim6&g0L{kA5o86Y-jBR-Ucnza*u5K^et)966}UV6W__?GbT zMaRUja&o@+cl)!x@~Uv1*&*apV_phr6e&n8E*7QB*S7^9{yT`J#7R<6DZ`%1EV1f1 zqi_Nc%_0@3;KRtdlSwf(r|NM^G(!+6T{@|0VEkvD&lhwWW0M=AL+_d2=NipPWkw*k ze9BwXY0Jt}QUpgS7aL0w927XWd|KPm3nKfz{b1gv%L8V;G`SId$o(Psqyd-dqoTPV z6LRx&$hKvzk2XtFmj&hemgyW8YK!dEmp6n#fB_CtC0=a*>cIZwokh+#OEF!%z|jmXwi^ zDN1w^QMs&4R6<(T6YccH@}93R_q6h@4%+l}r7}eE-F-0ysHH_De?}9Pp639AYceXMrsd89ijY{e$Tc;bZ@Dhi~Hop(lcp9QvoF9rn<6@OB7Uxit{-nAeX$)u}t z$^rlV`*$Q)<;DHcq3&+A-v=uyqU=!(K4MY8b$_vvY&V7b+G*qg(gk(OM};;ue7#}; zkqITFChOh86kj`*r-s8#U$PRqLVTjK|Lg+EJI;fXkUa$VIRl&9h9#0P-%E)BdOIr=MdM<{S^jte(7Wrta45#}D)6 zOCk}Ogo1-B`9MD{sJmqeZ(`(45(kL|vd!SI=uXcVI0j^wN5VwR%(FMh`o3@wJv{NP zur>W?+_ju=3J6&-GvDG1qh=Dxl*%FS6=KL>3*H3tlT-7~t}cPyl{OZ1)fdoky6RdmS={l3JuHP83SadoXMkQpPry zCu!voK`{2vW_@mfwQ+W41|8&}8Mma#BSXP{g5e^7$jdxfX~q9gVlRmELKE~!)`B*W z$ODZ|8hBjs#6#B+&!UNnMd#t-TWG^g-+|cd!q77Z*7wfCyUOn%)xW>3Dii zbnWbocCF#=qHJh7zXgkvOb?)XC!VN&lc+n17jEkr4pWq{{h%!a)#T7UEf7mpG27Ii zc>G#LQ8MDiVW}!AeIYguEq?=p)U?!tlff;G>OYFZs3>1vU4;e*2WLtVflhV{{@@NMmXl%sE*Qndb)jc+oRC&6nx$RKD6kyuCc)Izm-Rhh7(=py$nxTW0uRP)_5*2bV`1ii;gKWkv*+!sOhX zmZ#?d5t&Ark-e7}aItZbNok4}sB<8AdX+#%r!#jO3@qHZ!w!e*Zt(@}nAX(~Z1_SA zP&$0Hz}3htgY0vK;OVW$v2E{8{y0J_?y-@8zWna=Q>t>hb0Z5;#jv9mol3&e!S8GZxZPQayw=a^4Ua>F%o3LW9WS*&MKP@|MmYeZj)}V2}>e zcQXSvohC#i82;k0;78OA2S)*VXl+>2D~V!cZ00}&SUDmdXU*G|$1M!?Ayy9LkkT+S zRxz(~YQOjW=~K~l2LjuThL#q$xTv&~Bk<;aj3{hi-4bE?FUr||FMHECh!gKu@WKvw8Nx3x!R{ya8j&cuBFPI1r(UI6Z>@ zf}!f)vZk4w>V8hZ`5{O{BtQsMnB2t7?Pms7*uqfhDO`h|t`b4cs2l>J3;Z44MvQ9p zh2sCp`ihmj{%3DSvO6;;r^Z)NUNX&BIy>iAtVmUsg6GU?w-j#@qtU}8pd?vhYJ*^5 zdCl%Vk_4W>yU2skEnW>bujk1v7He$1mo3z6%9;LM6C>Gr=P^m@&`)W?g2S;~`fc|4 zOFDA6%TkZU`Nq+2-)!e2VY%Zm4}vhDtXZ}qhDMKddAh2-#BA=Ey;voBw7oB}TQB<) z{j#1XbD!J=p%%Xqv|p(XU@2FtCt?+E?r^CVz(ActnR(3yG59(X#PpWgQp?l7dG}bo z;=3fn&wQB?t!`gNT5WqIqa1 zEFevk;}l4vpk5aaF6h=yz_IaKCbv8$XHqLlOC~SSP>%!!Q5;zScra=GuDljne`vp>LkQrwQp4fGO$kIne{#O5!D*qcXCT~!d9kc8%#4y zGt5)Dt1Q%94hc4N11LO>0R@*cV3WQFu}lvHRUYu{c>zxt9zQ43>iVD^Q66nYM3$!x z%J<`-NhE%x<4#*eFJkTY6ob*v{&uvxTl!Mlf`n7wgHJXa-Ad<@2+y8JIrls`v68wO zpjuB)Z!r9nCk0A=3;Rir9)y(OYq^OAx~yr}V=1S|Vd}S~HX>)b&?ry?NYUP4`kHn( zu}G6b4B+S7H?w>c^Z_VOXrm-AnN{Uv;kxeSC0Zw`j3_LY%bxlAfcM`jG^^WzLqtmj zbY_f=tV)GSTfTkgVcuZjN8!1nB6u<&9`J%!rAi<`LgAV)4_jP8JPuXa{@QzDfHo>} za@kvf%2ipQ{J!lFM0c+8k8(}gr9Qn#s{63NsYgB7ukY<}->Z^=HF$hegq7T_+mqUV zYjd^ll{4A8%yx(gF0j0c)h2*Wgc<6igpy?}%-uvHVv+WGryODlhw15Vl@bHKy!AMi zPO%>3L%+KqJK<6nkdOV)!pWyS1%)anBS*L!C!Oh>6g$OuOU`iBW~up-bX`(Z#chMo zD?1@EBTG+u@-4ERUbV1+yvSETu^#Qh&r!5?Y_qY0hRi9ltF-11?Pm7)NJF*PJ!okX zolVcxHC?zYsmj^798=4~8QpGk$61x_^}J=o@@p`6ZzO54dM`>*0`Scs?>w0Z9l2t1 zZVP<6e2P8R$Q0V&T1d&TzzA{~(^^0x(!lw9Kp%BkD(X%_tIw1KM2|i)zJ8HeP2WTO zFKa9PLgWWP4q=eE@a$A0tM225y_9!WwB5aMf=%gA_?O@1%U%+rBbk$ubP2l5V)h!m z&6U4THC&ayul^;>hf`71vznT*vCD5F%vdmT<+MLegHC~_%G5}ZR&#G4z|nC!_ON=6 zg<-fEwT|(lE%>|nI5f0wH+NXfDD7~JV`yM%{5~Vldyc2l6rRoa<-@#`#q)PYL3h)0 zoP~jo$(e-R`@$c`J5})PpA$6PWiwweXH_o>i)1W5czgC!;%dT7d139>&}7;v2E|Ef zby!z}cZYNG@I#BJoQVb6Jzh)vQT{IZ*5rDm@VwVT)`N(+qt^6M1NJPpAV*)!66Wj9 zcZ*w8Xz=)_my4dKu;_O@_Y<+PBzF}AzHoB;D0A1}5S57&vgIkH4()s%&nTQuwaW|l zxz8JnL)9Y%@OcdhNV)rM%g0ii-T9b1s5cOCDj}uRRN`ZuT8bik4xl>w;_ zvK-CjgfVeiYWs_*@#8&Z2S{%?1y!t%9cIn#ZD9JFm>zC=pPVi!evCc!3DV$G3(q*N>Bu9W$ zO@kOlN?r(fWJ=AaN&0gjvL`J1xlH(#ff`$H#k*s9yJuU&d6(194`x`A{&^czn(p1P zBqbedo1YWMkk732rW@k@r#-%F|0iK|S%UYh!2|v|DZjh33e~}PA3fNM?Urxu`Pq(9it4w8f&WmR6B7fucs$WXoh;TM!!()s`A6-}O zy3J@o4rJVWYe;$%9p_WbqBs%3XHKG~ZiN~QIvjfz=IJ``wo~KECZXsdA6_XhJy9)a z8QY`yb-zi@TnjV)N!}!XcqGaYUf+vu|7D5hLui|m@=NqY<{X+l(ar@IcX!Bq0v@#* z21C^a2X&tdu@||g$u$ohEc8PmvLe(W4sedbZt-diu zX5!}RYjqbm*}QH0cv7adpp;ajUt>#4(br33k>e0_nIIqi!Ib+MY~7M~S^W;5`CAN! z?(3(2FmG7p9iqPWXJ`Nd0;^Luvgv3>?W)$MGPnpXwIQ4)M#6?S-|5C;`GFj zKNrkPN25R(I2w`!W^Vs+pQ=OppZ|Mzt@Gp`U4G07%%S2<1Gp0@*%bZngLdA;~Br43J+6?&mX$-KSoFLKOUqu0fn1E*8RhkQiW+g?1ba$@q5xoC>>wI}S79z?-E zO?1CGxB}W-Y&Yyznhfbi@VCz_;VQ$Q(J=UM@`>!=4~3&5Ky(jP0a#CU@E0~ndt!iQ z0geTqN350~LbOVPVe0CR=CM(wnYAU<@^jO5S8UnmIMDchjAmbg!$K#21nE2|S$m>s zWGS=g1sf{X14BktlQ)%1I(d0ufGGdIdlnnG;V6P9M3FVgAY!>;gg|UR!CRe*PtqnYK4P=<;9U02cgPE9e= z5dSBIKwpUmulxOrKC|3T>_qOKvshn!cWXJZQQ3O*=7||MqkHta>o(3h?9K1FB;C~+ zBw%0r?SmZ(R;Ikma6#}7?$YoFerbQ2*o`ce!Y;nO`#o!$EBfWpoBe9@_Vc8b{{2lY ze#E5zEeC^sKL#4_RS+XR{w2>S22#PB(rp{8M2{yarR1Zx3^!4_QLJu94~H-29I}7b zP*|m;OrGS9;uxA6eyIBL*f6j76BbpN5kGk{!^(?Ts)f+VBVX@V>inJ&u|8?6$ZBwR zvM*F2gfDFWC{OEsjtcaKqYWbsz7r*#YsLgfD(GMO*1GxHeI~G$8aqm+$2Tg`+YsUc5lT1=laj$7pfTd#vi?o+LFpoV z8s7rLHpCxX5i-%NFpIc^-Y$Yj7vy3=du?UL6_?+~xNPH9S)=wmXTiS~s=wze9jIY? zkK?57&M=x=t2;Uz#^PB4fWZR;^WzK;S~4<2yEMDn5+T-7%CY8eREW0Y2Pyc7MPHuO zud$CGoeWsTY#)z5e$oe*TXLDvyY=c$v43HTnT4 z!g76nXwp;5aWs+mGLII-GjRHa&l^)*KZlkJ1Ct?{8BEkCKtE`Rw~TMxAoJ0Is~P>; z^K3PyUa-0tH>5OqEPq10-<|DPEvm5fu|gj2=jl2R|D{Ucx#s-0YDS-MQJN+33a%v^Om2ygpyw!s>GIeq!D06C-T9ZqgPi!6f8t1pPf6qn^q66CB zI*fgqc&n09-xKy!v>>NOMA%nU8WEoWo+4!961GT!hcq(k+)os*e(L6~MKL!T`twV3 z^lL9qyu1B$GY@-=iRYdKU=nI>V5f;zUDfyM*!&B?X^`$@-Y*B#=rio>VS0eoph#!U z$1jeqWbxa6qvxxGO7knmJ|3>_TVOU&D&brk5*W2iCPb(4S2{{)9Xf5z{TN6 ziaZ2DDaoU$M@Hb3F5h%H%Jyr&IIs4a`{TLsaz{XnotCQBP3HD1+8m#uitgcrYylUcP!W4f7CVT;=Cp_TBdbqj((>{$ zr2_pr*}b|g);*?Pmn23(uPg!BkISosuhQfQc-yX)0@AG;O>J{cjtJgljQwnINZ>IN zhaA1K8M$?T06ny%8A8>TzD6h{m0RP^v8>c(wbGd)+H?&tWG`0o+^%mo><$l?(RTTK z=7x>PvNh&HS{iAncFYtkv-kcXno(-Z@1QexSDAv8HOGuUq z{grUI=d2T~80tX78}ZCQA?1&PorXM!6fyv?%mou#&+}Gs+w5x zvxu`VbciXB7v!M&LdK%7}`6goskP6N-627~saUvOopqJndo+{PYx%)b5=f}stQ zWL1ZL(sPOjP3GEQ0(``F%r vW)D{)1e^SBM~D_36y@n1*18#dMX$L=Zd1)~QimkoAY3{c2zZscZTSBHzBmcp literal 0 HcmV?d00001 diff --git a/cockatrice/resources/tips/tips_of_the_day.xml b/cockatrice/resources/tips/tips_of_the_day.xml index 5625fbe0..aa2cb463 100644 --- a/cockatrice/resources/tips/tips_of_the_day.xml +++ b/cockatrice/resources/tips/tips_of_the_day.xml @@ -83,4 +83,10 @@ face_down.png 2018-03-01 + + Counter expressions + When setting a counter value, you can type a math expression in the box and the counter will be set to the result.<br>The "x" variable contains the current counter value. + counter_expression.png + 2019-02-02 + \ No newline at end of file diff --git a/cockatrice/src/abstractcounter.cpp b/cockatrice/src/abstractcounter.cpp index 42cf6ad8..d9095138 100644 --- a/cockatrice/src/abstractcounter.cpp +++ b/cockatrice/src/abstractcounter.cpp @@ -1,9 +1,11 @@ #include "abstractcounter.h" +#include "expression.h" #include "pb/command_inc_counter.pb.h" #include "pb/command_set_counter.pb.h" #include "player.h" #include "settingscache.h" #include +#include #include #include #include @@ -17,7 +19,7 @@ AbstractCounter::AbstractCounter(Player *_player, bool _useNameForShortcut, QGraphicsItem *parent) : QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value), - useNameForShortcut(_useNameForShortcut), hovered(false), aDec(0), aInc(0), dialogSemaphore(false), + useNameForShortcut(_useNameForShortcut), hovered(false), aDec(nullptr), aInc(nullptr), dialogSemaphore(false), deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea) { setAcceptHoverEvents(true); @@ -114,7 +116,11 @@ void AbstractCounter::setValue(int _value) void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (isUnderMouse() && player->getLocal()) { - if (event->button() == Qt::LeftButton) { + if (event->button() == Qt::MidButton || (QApplication::keyboardModifiers() & Qt::ShiftModifier)) { + if (menu) + menu->exec(event->screenPos()); + event->accept(); + } else if (event->button() == Qt::LeftButton) { Command_IncCounter cmd; cmd.set_counter_id(id); cmd.set_delta(1); @@ -126,10 +132,6 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event) cmd.set_delta(-1); player->sendGameCommand(cmd); event->accept(); - } else if (event->button() == Qt::MidButton) { - if (menu) - menu->exec(event->screenPos()); - event->accept(); } } else event->ignore(); @@ -160,8 +162,12 @@ void AbstractCounter::setCounter() { bool ok; dialogSemaphore = true; - int newValue = QInputDialog::getInt(0, tr("Set counter"), tr("New value for counter '%1':").arg(name), value, - -2000000000, 2000000000, 1, &ok); + QString expression = QInputDialog::getText(nullptr, tr("Set counter"), tr("New value for counter '%1':").arg(name), + QLineEdit::Normal, QString::number(value), &ok); + + Expression exp(value); + int newValue = static_cast(exp.parse(expression)); + if (deleteAfterDialog) { deleteLater(); return; diff --git a/cockatrice/src/abstractcounter.h b/cockatrice/src/abstractcounter.h index 99bcd7ca..2e592348 100644 --- a/cockatrice/src/abstractcounter.h +++ b/cockatrice/src/abstractcounter.h @@ -11,6 +11,7 @@ class AbstractCounter : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) + protected: Player *player; int id; @@ -18,15 +19,17 @@ protected: int value; bool useNameForShortcut, hovered; - void mousePressEvent(QGraphicsSceneMouseEvent *event); - void hoverEnterEvent(QGraphicsSceneHoverEvent *event); - void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; private: QAction *aSet, *aDec, *aInc; QMenu *menu; bool dialogSemaphore, deleteAfterDialog; bool shownInCounterArea; + bool shortcutActive; + private slots: void refreshShortcuts(); void incrementCounter(); @@ -39,14 +42,19 @@ public: bool _shownInCounterArea, int _value, bool _useNameForShortcut = false, - QGraphicsItem *parent = 0); - ~AbstractCounter(); + QGraphicsItem *parent = nullptr); + ~AbstractCounter() override; + + void retranslateUi(); + void setValue(int _value); + void setShortcutsActive(); + void setShortcutsInactive(); + void delCounter(); QMenu *getMenu() const { return menu; } - void retranslateUi(); int getId() const { @@ -64,12 +72,6 @@ public: { return value; } - void setValue(int _value); - void delCounter(); - - void setShortcutsActive(); - void setShortcutsInactive(); - bool shortcutActive; }; #endif diff --git a/cockatrice/src/dlg_tip_of_the_day.cpp b/cockatrice/src/dlg_tip_of_the_day.cpp index 67ecc400..37d6465f 100644 --- a/cockatrice/src/dlg_tip_of_the_day.cpp +++ b/cockatrice/src/dlg_tip_of_the_day.cpp @@ -13,7 +13,7 @@ #define MIN_TIP_IMAGE_HEIGHT 200 #define MIN_TIP_IMAGE_WIDTH 200 #define MAX_TIP_IMAGE_HEIGHT 300 -#define MAX_TIP_IMAGE_WIDTH 300 +#define MAX_TIP_IMAGE_WIDTH 500 DlgTipOfTheDay::DlgTipOfTheDay(QWidget *parent) : QDialog(parent) { @@ -149,9 +149,9 @@ void DlgTipOfTheDay::updateTip(int tipId) qDebug() << "Image failed to load from" << imagePath; imageLabel->clear(); } else { - int h = std::min(std::max(image->height(), MIN_TIP_IMAGE_HEIGHT), MAX_TIP_IMAGE_HEIGHT); + int h = std::min(std::max(imageLabel->height(), MIN_TIP_IMAGE_HEIGHT), MAX_TIP_IMAGE_HEIGHT); int w = std::min(std::max(imageLabel->width(), MIN_TIP_IMAGE_WIDTH), MAX_TIP_IMAGE_WIDTH); - imageLabel->setPixmap(image->scaled(h, w, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imageLabel->setPixmap(image->scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } date->setText("Tip added on: " + tip.getDate().toString("yyyy.MM.dd") + ""); @@ -163,9 +163,7 @@ void DlgTipOfTheDay::updateTip(int tipId) void DlgTipOfTheDay::resizeEvent(QResizeEvent *event) { - int h = imageLabel->height(); - int w = imageLabel->width(); - imageLabel->setPixmap(image->scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imageLabel->setPixmap(image->scaled(imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); QWidget::resizeEvent(event); } diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index f32d59a2..cf04f195 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -26,6 +26,7 @@ SET(common_SOURCES server_room.cpp serverinfo_user_container.cpp sfmt/SFMT.c + expression.cpp ) set(ORACLE_LIBS) diff --git a/common/expression.cpp b/common/expression.cpp new file mode 100644 index 00000000..1671d04b --- /dev/null +++ b/common/expression.cpp @@ -0,0 +1,108 @@ +#include "expression.h" +#include "./lib/peglib.h" + +#include +#include +#include +#include + +peg::parser math(R"( + EXPRESSION <- P0 + P0 <- P1 (P1_OPERATOR P1)* + P1 <- P2 (P2_OPERATOR P2)* + P2 <- P3 (P3_OPERATOR P3)* + P3 <- NUMBER / FUNCTION / VARIABLE / '(' P0 ')' + + P1_OPERATOR <- < [-+] > + P2_OPERATOR <- < [/*] > + P3_OPERATOR <- < '^' > + + NUMBER <- < '-'? [0-9]+ > + NAME <- < [a-z][a-z0-9]* > + VARIABLE <- < [x] > + FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')' + + %whitespace <- [ \t\r]* + )"); + +QMap> *default_functions = nullptr; + +Expression::Expression(double initial) : value(initial) +{ + if (default_functions == nullptr) { + default_functions = new QMap>(); + default_functions->insert("sin", [](double a) { return sin(a); }); + default_functions->insert("cos", [](double a) { return cos(a); }); + default_functions->insert("tan", [](double a) { return tan(a); }); + default_functions->insert("sqrt", [](double a) { return sqrt(a); }); + default_functions->insert("log", [](double a) { return log(a); }); + default_functions->insert("log10", [](double a) { return log(a); }); + default_functions->insert("trunc", [](double a) { return trunc(a); }); + default_functions->insert("abs", [](double a) { return abs(a); }); + + default_functions->insert("floor", [](double a) { return floor(a); }); + default_functions->insert("ceil", [](double a) { return ceil(a); }); + default_functions->insert("round", [](double a) { return round(a); }); + default_functions->insert("trunc", [](double a) { return trunc(a); }); + } + fns = QMap>(*default_functions); +} + +double Expression::eval(const peg::Ast &ast) +{ + const auto &nodes = ast.nodes; + if (ast.name == "NUMBER") { + return stod(ast.token); + } else if (ast.name == "FUNCTION") { + QString name = QString::fromStdString(nodes[0]->token); + if (!fns.contains(name)) + return 0; + return fns[name](eval(*nodes[1])); + } else if (ast.name == "VARIABLE") { + return value; + } else if (ast.name[0] == 'P') { + double result = eval(*nodes[0]); + for (int i = 1; i < nodes.size(); i += 2) { + double arg = eval(*nodes[i + 1]); + char operation = nodes[i]->token[0]; + switch (operation) { + case '+': + result += arg; + break; + case '-': + result -= arg; + break; + case '*': + result *= arg; + break; + case '/': + result /= arg; + break; + case '^': + result = pow(result, arg); + break; + default: + result = 0; + break; + } + } + return result; + } else { + return -1; + } +} + +double Expression::parse(const QString &expr) +{ + QByteArray ba = expr.toLocal8Bit(); + + math.enable_ast(); + + std::shared_ptr ast; + if (math.parse(ba.data(), ast)) { + ast = peg::AstOptimizer(true).optimize(ast); + return eval(*ast); + } + + return 0; +} diff --git a/common/expression.h b/common/expression.h new file mode 100644 index 00000000..8c6a9493 --- /dev/null +++ b/common/expression.h @@ -0,0 +1,28 @@ +#ifndef EXPRESSION_H +#define EXPRESSION_H + +#include +#include +#include + +namespace peg +{ +template struct AstBase; +struct EmptyType; +typedef AstBase Ast; +} // namespace peg + +class Expression +{ +public: + double value; + + explicit Expression(double initial = 0); + double parse(const QString &expr); + +private: + double eval(const peg::Ast &ast); + QMap> fns; +}; + +#endif diff --git a/common/lib/peglib.h b/common/lib/peglib.h new file mode 100644 index 00000000..5c5e4c01 --- /dev/null +++ b/common/lib/peglib.h @@ -0,0 +1,3293 @@ +// +// peglib.h +// +// Copyright (c) 2015-18 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPPEGLIB_PEGLIB_H +#define CPPPEGLIB_PEGLIB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// guard for older versions of VC++ +#ifdef _MSC_VER +// VS2013 has no constexpr +#if (_MSC_VER == 1800) +#define PEGLIB_NO_CONSTEXPR_SUPPORT +#elif (_MSC_VER >= 1800) +// good to go +#else (_MSC_VER < 1800) +#error "Requires C+11 support" +#endif +#endif + +// define if the compiler doesn't support unicode characters reliably in the +// source code +//#define PEGLIB_NO_UNICODE_CHARS + +namespace peg { + +/*----------------------------------------------------------------------------- + * any + *---------------------------------------------------------------------------*/ + +class any +{ +public: + any() : content_(nullptr) {} + + any(const any& rhs) : content_(rhs.clone()) {} + + any(any&& rhs) : content_(rhs.content_) { + rhs.content_ = nullptr; + } + + template + any(const T& value) : content_(new holder(value)) {} + + any& operator=(const any& rhs) { + if (this != &rhs) { + if (content_) { + delete content_; + } + content_ = rhs.clone(); + } + return *this; + } + + any& operator=(any&& rhs) { + if (this != &rhs) { + if (content_) { + delete content_; + } + content_ = rhs.content_; + rhs.content_ = nullptr; + } + return *this; + } + + ~any() { + delete content_; + } + + bool is_undefined() const { + return content_ == nullptr; + } + + template < + typename T, + typename std::enable_if::value, std::nullptr_t>::type = nullptr + > + T& get() { + if (!content_) { + throw std::bad_cast(); + } + auto p = dynamic_cast*>(content_); + assert(p); + if (!p) { + throw std::bad_cast(); + } + return p->value_; + } + + template < + typename T, + typename std::enable_if::value, std::nullptr_t>::type = nullptr + > + T& get() { + return *this; + } + + template < + typename T, + typename std::enable_if::value, std::nullptr_t>::type = nullptr + > + const T& get() const { + assert(content_); + auto p = dynamic_cast*>(content_); + assert(p); + if (!p) { + throw std::bad_cast(); + } + return p->value_; + } + + template < + typename T, + typename std::enable_if::value, std::nullptr_t>::type = nullptr + > + const any& get() const { + return *this; + } + +private: + struct placeholder { + virtual ~placeholder() {} + virtual placeholder* clone() const = 0; + }; + + template + struct holder : placeholder { + holder(const T& value) : value_(value) {} + placeholder* clone() const override { + return new holder(value_); + } + T value_; + }; + + placeholder* clone() const { + return content_ ? content_->clone() : nullptr; + } + + placeholder* content_; +}; + +/*----------------------------------------------------------------------------- + * scope_exit + *---------------------------------------------------------------------------*/ + +// This is based on "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". + +template +struct scope_exit +{ + explicit scope_exit(EF&& f) + : exit_function(std::move(f)) + , execute_on_destruction{true} {} + + scope_exit(scope_exit&& rhs) + : exit_function(std::move(rhs.exit_function)) + , execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } + + ~scope_exit() { + if (execute_on_destruction) { + this->exit_function(); + } + } + + void release() { + this->execute_on_destruction = false; + } + +private: + scope_exit(const scope_exit&) = delete; + void operator=(const scope_exit&) = delete; + scope_exit& operator=(scope_exit&&) = delete; + + EF exit_function; + bool execute_on_destruction; +}; + +template +auto make_scope_exit(EF&& exit_function) -> scope_exit { + return scope_exit::type>(std::forward(exit_function)); +} + +/*----------------------------------------------------------------------------- + * UTF8 functions + *---------------------------------------------------------------------------*/ + +inline size_t codepoint_length(const char *s8, size_t l) { + if (l) { + auto b = static_cast(s8[0]); + if ((b & 0x80) == 0) { + return 1; + } else if ((b & 0xE0) == 0xC0) { + return 2; + } else if ((b & 0xF0) == 0xE0) { + return 3; + } else if ((b & 0xF8) == 0xF0) { + return 4; + } + } + return 0; +} + +inline size_t encode_codepoint(char32_t cp, char *buff) { + if (cp < 0x0080) { + buff[0] = static_cast(cp & 0x7F); + return 1; + } else if (cp < 0x0800) { + buff[0] = static_cast(0xC0 | ((cp >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (cp & 0x3F)); + return 2; + } else if (cp < 0xD800) { + buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (cp & 0x3F)); + return 3; + } else if (cp < 0xE000) { + // D800 - DFFF is invalid... + return 0; + } else if (cp < 0x10000) { + buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (cp & 0x3F)); + return 3; + } else if (cp < 0x110000) { + buff[0] = static_cast(0xF0 | ((cp >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((cp >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (cp & 0x3F)); + return 4; + } + return 0; +} + +inline std::string encode_codepoint(char32_t cp) { + char buff[4]; + auto l = encode_codepoint(cp, buff); + return std::string(buff, l); +} + +inline bool decode_codepoint(const char *s8, size_t l, size_t &bytes, + char32_t &cp) { + if (l) { + auto b = static_cast(s8[0]); + if ((b & 0x80) == 0) { + bytes = 1; + cp = b; + return true; + } else if ((b & 0xE0) == 0xC0) { + if (l >= 2) { + bytes = 2; + cp = ((static_cast(s8[0] & 0x1F)) << 6) | + (static_cast(s8[1] & 0x3F)); + return true; + } + } else if ((b & 0xF0) == 0xE0) { + if (l >= 3) { + bytes = 3; + cp = ((static_cast(s8[0] & 0x0F)) << 12) | + ((static_cast(s8[1] & 0x3F)) << 6) | + (static_cast(s8[2] & 0x3F)); + return true; + } + } else if ((b & 0xF8) == 0xF0) { + if (l >= 4) { + bytes = 4; + cp = ((static_cast(s8[0] & 0x07)) << 18) | + ((static_cast(s8[1] & 0x3F)) << 12) | + ((static_cast(s8[2] & 0x3F)) << 6) | + (static_cast(s8[3] & 0x3F)); + return true; + } + } + } + return false; +} + +inline size_t decode_codepoint(const char *s8, size_t l, char32_t &out) { + size_t bytes; + if (decode_codepoint(s8, l, bytes, out)) { + return bytes; + } + return 0; +} + +inline char32_t decode_codepoint(const char *s8, size_t l) { + char32_t out = 0; + decode_codepoint(s8, l, out); + return out; +} + +inline std::u32string decode(const char *s8, size_t l) { + std::u32string out; + size_t i = 0; + while (i < l) { + auto beg = i++; + while (i < l && (s8[i] & 0xc0) == 0x80) { + i++; + } + out += decode_codepoint(&s8[beg], (i - beg)); + } + return out; +} + +/*----------------------------------------------------------------------------- + * resolve_escape_sequence + *---------------------------------------------------------------------------*/ + +inline bool is_hex(char c, int& v) { + if ('0' <= c && c <= '9') { + v = c - '0'; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } + return false; +} + +inline bool is_digit(char c, int& v) { + if ('0' <= c && c <= '9') { + v = c - '0'; + return true; + } + return false; +} + +inline std::pair parse_hex_number(const char* s, size_t n, size_t i) { + int ret = 0; + int val; + while (i < n && is_hex(s[i], val)) { + ret = static_cast(ret * 16 + val); + i++; + } + return std::make_pair(ret, i); +} + +inline std::pair parse_octal_number(const char* s, size_t n, size_t i) { + int ret = 0; + int val; + while (i < n && is_digit(s[i], val)) { + ret = static_cast(ret * 8 + val); + i++; + } + return std::make_pair(ret, i); +} + +inline std::string resolve_escape_sequence(const char* s, size_t n) { + std::string r; + r.reserve(n); + + size_t i = 0; + while (i < n) { + auto ch = s[i]; + if (ch == '\\') { + i++; + switch (s[i]) { + case 'n': r += '\n'; i++; break; + case 'r': r += '\r'; i++; break; + case 't': r += '\t'; i++; break; + case '\'': r += '\''; i++; break; + case '"': r += '"'; i++; break; + case '[': r += '['; i++; break; + case ']': r += ']'; i++; break; + case '\\': r += '\\'; i++; break; + case 'x': + case 'u': { + char32_t cp; + std::tie(cp, i) = parse_hex_number(s, n, i + 1); + r += encode_codepoint(cp); + break; + } + default: { + char32_t cp; + std::tie(cp, i) = parse_octal_number(s, n, i); + r += encode_codepoint(cp); + break; + } + } + } else { + r += ch; + i++; + } + } + return r; +} + +/*----------------------------------------------------------------------------- + * PEG + *---------------------------------------------------------------------------*/ + +/* +* Line information utility function +*/ +inline std::pair line_info(const char* start, const char* cur) { + auto p = start; + auto col_ptr = p; + auto no = 1; + + while (p < cur) { + if (*p == '\n') { + no++; + col_ptr = p + 1; + } + p++; + } + + auto col = p - col_ptr + 1; + + return std::make_pair(no, col); +} + +/* +* Semantic values +*/ +struct SemanticValues : protected std::vector +{ + // Input text + const char* path; + const char* ss; + + // Matched string + const char* c_str() const { return s_; } + size_t length() const { return n_; } + + std::string str() const { + return std::string(s_, n_); + } + + // Line number and column at which the matched string is + std::pair line_info() const { + return peg::line_info(ss, s_); + } + + // Choice count + size_t choice_count() const { return choice_count_; } + + // Choice number (0 based index) + size_t choice() const { return choice_; } + + // Tokens + std::vector> tokens; + + std::string token(size_t id = 0) const { + if (!tokens.empty()) { + assert(id < tokens.size()); + const auto& tok = tokens[id]; + return std::string(tok.first, tok.second); + } + return std::string(s_, n_); + } + + // Transform the semantic value vector to another vector + template + auto transform(size_t beg = 0, size_t end = static_cast(-1)) const -> vector { + return this->transform(beg, end, [](const any& v) { return v.get(); }); + } + + SemanticValues() : s_(nullptr), n_(0), choice_count_(0), choice_(0) {} + + using std::vector::iterator; + using std::vector::const_iterator; + using std::vector::size; + using std::vector::empty; + using std::vector::assign; + using std::vector::begin; + using std::vector::end; + using std::vector::rbegin; + using std::vector::rend; + using std::vector::operator[]; + using std::vector::at; + using std::vector::resize; + using std::vector::front; + using std::vector::back; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::insert; + using std::vector::erase; + using std::vector::clear; + using std::vector::swap; + using std::vector::emplace; + using std::vector::emplace_back; + +private: + friend class Context; + friend class Sequence; + friend class PrioritizedChoice; + friend class Holder; + + const char* s_; + size_t n_; + size_t choice_count_; + size_t choice_; + + template + auto transform(F f) const -> vector::type> { + vector::type> r; + for (const auto& v: *this) { + r.emplace_back(f(v)); + } + return r; + } + + template + auto transform(size_t beg, size_t end, F f) const -> vector::type> { + vector::type> r; + end = (std::min)(end, size()); + for (size_t i = beg; i < end; i++) { + r.emplace_back(f((*this)[i])); + } + return r; + } + + void reset() { + path = nullptr; + ss = nullptr; + tokens.clear(); + + s_ = nullptr; + n_ = 0; + choice_count_ = 0; + choice_ = 0; + } +}; + +/* + * Semantic action + */ +template < + typename R, typename F, + typename std::enable_if::value, std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args&&... args) { + fn(std::forward(args)...); + return any(); +} + +template < + typename R, typename F, + typename std::enable_if::type, any>::value, std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args&&... args) { + return fn(std::forward(args)...); +} + +template < + typename R, typename F, + typename std::enable_if< + !std::is_void::value && + !std::is_same::type, any>::value, std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args&&... args) { + return any(fn(std::forward(args)...)); +} + +class Action +{ +public: + Action() = default; + + Action(const Action& rhs) : fn_(rhs.fn_) {} + + template ::value && !std::is_same::value, std::nullptr_t>::type = nullptr> + Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} + + template ::value, std::nullptr_t>::type = nullptr> + Action(F fn) : fn_(make_adaptor(fn, fn)) {} + + template ::value, std::nullptr_t>::type = nullptr> + Action(F /*fn*/) {} + + template ::value && !std::is_same::value, std::nullptr_t>::type = nullptr> + void operator=(F fn) { + fn_ = make_adaptor(fn, &F::operator()); + } + + template ::value, std::nullptr_t>::type = nullptr> + void operator=(F fn) { + fn_ = make_adaptor(fn, fn); + } + + template ::value, std::nullptr_t>::type = nullptr> + void operator=(F /*fn*/) {} + + Action& operator=(const Action& rhs) = default; + + operator bool() const { + return bool(fn_); + } + + any operator()(SemanticValues& sv, any& dt) const { + return fn_(sv, dt); + } + +private: + template + struct TypeAdaptor_sv { + TypeAdaptor_sv(std::function fn) + : fn_(fn) {} + any operator()(SemanticValues& sv, any& /*dt*/) { + return call(fn_, sv); + } + std::function fn_; + }; + + template + struct TypeAdaptor_csv { + TypeAdaptor_csv(std::function fn) + : fn_(fn) {} + any operator()(SemanticValues& sv, any& /*dt*/) { + return call(fn_, sv); + } + std::function fn_; + }; + + template + struct TypeAdaptor_sv_dt { + TypeAdaptor_sv_dt(std::function fn) + : fn_(fn) {} + any operator()(SemanticValues& sv, any& dt) { + return call(fn_, sv, dt); + } + std::function fn_; + }; + + template + struct TypeAdaptor_csv_dt { + TypeAdaptor_csv_dt(std::function fn) + : fn_(fn) {} + any operator()(SemanticValues& sv, any& dt) { + return call(fn_, sv, dt); + } + std::function fn_; + }; + + typedef std::function Fty; + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv) const) { + return TypeAdaptor_sv(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv) const) { + return TypeAdaptor_csv(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv)) { + return TypeAdaptor_sv(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv)) { + return TypeAdaptor_csv(fn); + } + + template + Fty make_adaptor(F fn, R (* /*mf*/)(SemanticValues& sv)) { + return TypeAdaptor_sv(fn); + } + + template + Fty make_adaptor(F fn, R (* /*mf*/)(const SemanticValues& sv)) { + return TypeAdaptor_csv(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt) const) { + return TypeAdaptor_sv_dt(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt) const) { + return TypeAdaptor_csv_dt(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt)) { + return TypeAdaptor_sv_dt(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt)) { + return TypeAdaptor_csv_dt(fn); + } + + template + Fty make_adaptor(F fn, R(* /*mf*/)(SemanticValues& sv, any& dt)) { + return TypeAdaptor_sv_dt(fn); + } + + template + Fty make_adaptor(F fn, R(* /*mf*/)(const SemanticValues& sv, any& dt)) { + return TypeAdaptor_csv_dt(fn); + } + + Fty fn_; +}; + +/* + * Semantic predicate + */ +// Note: 'parse_error' exception class should be be used in sematic action handlers to reject the rule. +struct parse_error { + parse_error() = default; + parse_error(const char* s) : s_(s) {} + const char* what() const { return s_.empty() ? nullptr : s_.c_str(); } +private: + std::string s_; +}; + +/* + * Result + */ +inline bool success(size_t len) { + return len != static_cast(-1); +} + +inline bool fail(size_t len) { + return len == static_cast(-1); +} + +/* + * Context + */ +class Context; +class Ope; +class Definition; + +typedef std::function Tracer; + +class Context +{ +public: + const char* path; + const char* s; + const size_t l; + + const char* error_pos; + const char* message_pos; + std::string message; // TODO: should be `int`. + + std::vector> value_stack; + size_t value_stack_size; + std::vector>> args_stack; + + size_t nest_level; + + bool in_token; + + std::shared_ptr whitespaceOpe; + bool in_whitespace; + + std::shared_ptr wordOpe; + + std::vector> capture_scope_stack; + + const size_t def_count; + const bool enablePackratParsing; + std::vector cache_registered; + std::vector cache_success; + + std::map, std::tuple> cache_values; + + std::function tracer; + + Context( + const char* a_path, + const char* a_s, + size_t a_l, + size_t a_def_count, + std::shared_ptr a_whitespaceOpe, + std::shared_ptr a_wordOpe, + bool a_enablePackratParsing, + Tracer a_tracer) + : path(a_path) + , s(a_s) + , l(a_l) + , error_pos(nullptr) + , message_pos(nullptr) + , value_stack_size(0) + , nest_level(0) + , in_token(false) + , whitespaceOpe(a_whitespaceOpe) + , in_whitespace(false) + , wordOpe(a_wordOpe) + , def_count(a_def_count) + , enablePackratParsing(a_enablePackratParsing) + , cache_registered(enablePackratParsing ? def_count * (l + 1) : 0) + , cache_success(enablePackratParsing ? def_count * (l + 1) : 0) + , tracer(a_tracer) + { + args_stack.resize(1); + capture_scope_stack.resize(1); + } + + template + void packrat(const char* a_s, size_t def_id, size_t& len, any& val, T fn) { + if (!enablePackratParsing) { + fn(val); + return; + } + + auto col = a_s - s; + auto idx = def_count * static_cast(col) + def_id; + + if (cache_registered[idx]) { + if (cache_success[idx]) { + auto key = std::make_pair(col, def_id); + std::tie(len, val) = cache_values[key]; + return; + } else { + len = static_cast(-1); + return; + } + } else { + fn(val); + cache_registered[idx] = true; + cache_success[idx] = success(len); + if (success(len)) { + auto key = std::make_pair(col, def_id); + cache_values[key] = std::make_pair(len, val); + } + return; + } + } + + SemanticValues& push() { + assert(value_stack_size <= value_stack.size()); + if (value_stack_size == value_stack.size()) { + value_stack.emplace_back(std::make_shared()); + } + auto& sv = *value_stack[value_stack_size++]; + if (!sv.empty()) { + sv.clear(); + } + sv.reset(); + sv.path = path; + sv.ss = s; + return sv; + } + + void pop() { + value_stack_size--; + } + + void push_args(const std::vector>& args) { + args_stack.push_back(args); + } + + void pop_args() { + args_stack.pop_back(); + } + + const std::vector>& top_args() const { + return args_stack[args_stack.size() - 1]; + } + + void push_capture_scope() { + capture_scope_stack.resize(capture_scope_stack.size() + 1); + } + + void pop_capture_scope() { + capture_scope_stack.pop_back(); + } + + void shift_capture_values() { + assert(capture_scope_stack.size() >= 2); + auto it = capture_scope_stack.rbegin(); + auto it_prev = it + 1; + for (const auto& kv: *it) { + (*it_prev)[kv.first] = kv.second; + } + } + + void set_error_pos(const char* a_s) { + if (error_pos < a_s) error_pos = a_s; + } + + void trace(const char* name, const char* a_s, size_t n, SemanticValues& sv, any& dt) const { + if (tracer) tracer(name, a_s, n, sv, *this, dt); + } +}; + +/* + * Parser operators + */ +class Ope +{ +public: + struct Visitor; + + virtual ~Ope() {} + virtual size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const = 0; + virtual void accept(Visitor& v) = 0; +}; + +class Sequence : public Ope +{ +public: + Sequence(const Sequence& rhs) : opes_(rhs.opes_) {} + +#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 + // NOTE: Compiler Error C2797 on Visual Studio 2013 + // "The C++ compiler in Visual Studio does not implement list + // initialization inside either a member initializer list or a non-static + // data member initializer. Before Visual Studio 2013 Update 3, this was + // silently converted to a function call, which could lead to bad code + // generation. Visual Studio 2013 Update 3 reports this as an error." + template + Sequence(const Args& ...args) { + opes_ = std::vector>{ static_cast>(args)... }; + } +#else + template + Sequence(const Args& ...args) : opes_{ static_cast>(args)... } {} +#endif + + Sequence(const std::vector>& opes) : opes_(opes) {} + Sequence(std::vector>&& opes) : opes_(opes) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("Sequence", s, n, sv, dt); + auto& chldsv = c.push(); + size_t i = 0; + for (const auto& ope : opes_) { + c.nest_level++; + auto se = make_scope_exit([&]() { c.nest_level--; }); + const auto& rule = *ope; + auto len = rule.parse(s + i, n - i, chldsv, c, dt); + if (fail(len)) { + return static_cast(-1); + } + i += len; + } + sv.insert(sv.end(), chldsv.begin(), chldsv.end()); + sv.s_ = chldsv.c_str(); + sv.n_ = chldsv.length(); + sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); + return i; + } + + void accept(Visitor& v) override; + + std::vector> opes_; +}; + +class PrioritizedChoice : public Ope +{ +public: +#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 + // NOTE: Compiler Error C2797 on Visual Studio 2013 + // "The C++ compiler in Visual Studio does not implement list + // initialization inside either a member initializer list or a non-static + // data member initializer. Before Visual Studio 2013 Update 3, this was + // silently converted to a function call, which could lead to bad code + // generation. Visual Studio 2013 Update 3 reports this as an error." + template + PrioritizedChoice(const Args& ...args) { + opes_ = std::vector>{ static_cast>(args)... }; + } +#else + template + PrioritizedChoice(const Args& ...args) : opes_{ static_cast>(args)... } {} +#endif + + PrioritizedChoice(const std::vector>& opes) : opes_(opes) {} + PrioritizedChoice(std::vector>&& opes) : opes_(opes) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("PrioritizedChoice", s, n, sv, dt); + size_t id = 0; + for (const auto& ope : opes_) { + c.nest_level++; + auto& chldsv = c.push(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop(); + c.pop_capture_scope(); + }); + const auto& rule = *ope; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + sv.insert(sv.end(), chldsv.begin(), chldsv.end()); + sv.s_ = chldsv.c_str(); + sv.n_ = chldsv.length(); + sv.choice_count_ = opes_.size(); + sv.choice_ = id; + sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); + + c.shift_capture_values(); + return len; + } + id++; + } + return static_cast(-1); + } + + void accept(Visitor& v) override; + + size_t size() const { return opes_.size(); } + + std::vector> opes_; +}; + +class ZeroOrMore : public Ope +{ +public: + ZeroOrMore(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("ZeroOrMore", s, n, sv, dt); + auto save_error_pos = c.error_pos; + size_t i = 0; + while (n - i > 0) { + c.nest_level++; + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + const auto& rule = *ope_; + auto len = rule.parse(s + i, n - i, sv, c, dt); + if (success(len)) { + c.shift_capture_values(); + } else { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + break; + } + i += len; + } + return i; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class OneOrMore : public Ope +{ +public: + OneOrMore(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("OneOrMore", s, n, sv, dt); + size_t len = 0; + { + c.nest_level++; + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); + const auto& rule = *ope_; + len = rule.parse(s, n, sv, c, dt); + if (success(len)) { + c.shift_capture_values(); + } else { + return static_cast(-1); + } + } + auto save_error_pos = c.error_pos; + auto i = len; + while (n - i > 0) { + c.nest_level++; + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + const auto& rule = *ope_; + len = rule.parse(s + i, n - i, sv, c, dt); + if (success(len)) { + c.shift_capture_values(); + } else { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + break; + } + i += len; + } + return i; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class Option : public Ope +{ +public: + Option(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("Option", s, n, sv, dt); + auto save_error_pos = c.error_pos; + c.nest_level++; + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len)) { + c.shift_capture_values(); + return len; + } else { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + return 0; + } + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class AndPredicate : public Ope +{ +public: + AndPredicate(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("AndPredicate", s, n, sv, dt); + c.nest_level++; + auto& chldsv = c.push(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop(); + c.pop_capture_scope(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + return 0; + } else { + return static_cast(-1); + } + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class NotPredicate : public Ope +{ +public: + NotPredicate(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("NotPredicate", s, n, sv, dt); + auto save_error_pos = c.error_pos; + c.nest_level++; + auto& chldsv = c.push(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop(); + c.pop_capture_scope(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + c.set_error_pos(s); + return static_cast(-1); + } else { + c.error_pos = save_error_pos; + return 0; + } + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class LiteralString : public Ope + , public std::enable_shared_from_this +{ +public: + LiteralString(const std::string& s) + : lit_(s) + , init_is_word_(false) + , is_word_(false) + {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::string lit_; + mutable bool init_is_word_; + mutable bool is_word_; +}; + +class CharacterClass : public Ope + , public std::enable_shared_from_this +{ +public: + CharacterClass(const std::string& s) { + auto chars = decode(s.c_str(), s.length()); + auto i = 0u; + while (i < chars.size()) { + if (i + 2 < chars.size() && chars[i + 1] == '-') { + auto cp1 = chars[i]; + auto cp2 = chars[i + 2]; + ranges_.emplace_back(std::make_pair(cp1, cp2)); + i += 3; + } else { + auto cp = chars[i]; + ranges_.emplace_back(std::make_pair(cp, cp)); + i += 1; + } + } + } + + CharacterClass(const std::vector>& ranges) : ranges_(ranges) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("CharacterClass", s, n, sv, dt); + + if (n < 1) { + c.set_error_pos(s); + return static_cast(-1); + } + + char32_t cp; + auto len = decode_codepoint(s, n, cp); + + if (!ranges_.empty()) { + for (const auto& range: ranges_) { + if (range.first <= cp && cp <= range.second) { + return len; + } + } + } + + c.set_error_pos(s); + return static_cast(-1); + } + + void accept(Visitor& v) override; + + std::vector> ranges_; +}; + +class Character : public Ope + , public std::enable_shared_from_this +{ +public: + Character(char ch) : ch_(ch) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("Character", s, n, sv, dt); + if (n < 1 || s[0] != ch_) { + c.set_error_pos(s); + return static_cast(-1); + } + return 1; + } + + void accept(Visitor& v) override; + + char ch_; +}; + +class AnyCharacter : public Ope + , public std::enable_shared_from_this +{ +public: + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("AnyCharacter", s, n, sv, dt); + auto len = codepoint_length(s, n); + if (len < 1) { + c.set_error_pos(s); + return static_cast(-1); + } + return len; + } + + void accept(Visitor& v) override; +}; + +class CaptureScope : public Ope +{ +public: + CaptureScope(const std::shared_ptr& ope) + : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.pop_capture_scope(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + return len; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class Capture : public Ope +{ +public: + typedef std::function MatchAction; + + Capture(const std::shared_ptr& ope, MatchAction ma) + : ope_(ope), match_action_(ma) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len) && match_action_) { + match_action_(s, len, c); + } + return len; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; + MatchAction match_action_; +}; + +class TokenBoundary : public Ope +{ +public: + TokenBoundary(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class Ignore : public Ope +{ +public: + Ignore(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& /*sv*/, Context& c, any& dt) const override { + const auto& rule = *ope_; + auto& chldsv = c.push(); + auto se = make_scope_exit([&]() { + c.pop(); + }); + return rule.parse(s, n, chldsv, c, dt); + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +typedef std::function Parser; + +class User : public Ope +{ +public: + User(Parser fn) : fn_(fn) {} + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("User", s, n, sv, dt); + assert(fn_); + return fn_(s, n, sv, dt); + } + void accept(Visitor& v) override; + std::function fn_; +}; + +class WeakHolder : public Ope +{ +public: + WeakHolder(const std::shared_ptr& ope) : weak_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + auto ope = weak_.lock(); + assert(ope); + const auto& rule = *ope; + return rule.parse(s, n, sv, c, dt); + } + + void accept(Visitor& v) override; + + std::weak_ptr weak_; +}; + +class Holder : public Ope +{ +public: + Holder(Definition* outer) + : outer_(outer) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + any reduce(SemanticValues& sv, any& dt) const; + + std::shared_ptr ope_; + Definition* outer_; + + friend class Definition; +}; + +typedef std::unordered_map Grammar; + +class Reference : public Ope + , public std::enable_shared_from_this +{ +public: + Reference( + const Grammar& grammar, + const std::string& name, + const char* s, + bool is_macro, + const std::vector>& args) + : grammar_(grammar) + , name_(name) + , s_(s) + , is_macro_(is_macro) + , args_(args) + , rule_(nullptr) + , iarg_(0) + {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::shared_ptr get_core_operator() const; + + const Grammar& grammar_; + const std::string name_; + const char* s_; + + const bool is_macro_; + const std::vector> args_; + + Definition* rule_; + size_t iarg_; +}; + +class Whitespace : public Ope +{ +public: + Whitespace(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + if (c.in_whitespace) { + return 0; + } + c.in_whitespace = true; + auto se = make_scope_exit([&]() { c.in_whitespace = false; }); + const auto& rule = *ope_; + return rule.parse(s, n, sv, c, dt); + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class BackReference : public Ope +{ +public: + BackReference(const std::string& name) : name_(name) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::string name_; +}; + +/* + * Factories + */ +template +std::shared_ptr seq(Args&& ...args) { + return std::make_shared(static_cast>(args)...); +} + +template +std::shared_ptr cho(Args&& ...args) { + return std::make_shared(static_cast>(args)...); +} + +inline std::shared_ptr zom(const std::shared_ptr& ope) { + return std::make_shared(ope); +} + +inline std::shared_ptr oom(const std::shared_ptr& ope) { + return std::make_shared(ope); +} + +inline std::shared_ptr opt(const std::shared_ptr& ope) { + return std::make_shared