From b30f715623e7754d9ea7bcebc6bd987355a620a7 Mon Sep 17 00:00:00 2001 From: Sharadha Ramakrishnan Date: Wed, 7 Nov 2012 17:10:01 -0500 Subject: [PATCH] first milestone --- .chatlang.py.swp | Bin 0 -> 12288 bytes .hello.cl.swp | Bin 0 -> 12288 bytes ast_ds.py | 74 +++++++++++++++++++------------------- ast_ds.pyc | Bin 0 -> 7268 bytes chatlang.py | 19 ++++++++++ chatlang_lexer.py | 6 ++-- chatlang_lexer.pyc | Bin 0 -> 1059 bytes combinators.py | 22 ++++++------ combinators.pyc | Bin 0 -> 6262 bytes gen_lexer.pyc | Bin 0 -> 939 bytes hello.cl | 14 ++++---- parser.py | 87 +++++++++++++++++++++++++++++++++++---------- parser.pyc | Bin 0 -> 8039 bytes 13 files changed, 147 insertions(+), 75 deletions(-) create mode 100644 .chatlang.py.swp create mode 100644 .hello.cl.swp create mode 100644 ast_ds.pyc create mode 100755 chatlang.py create mode 100644 chatlang_lexer.pyc create mode 100644 combinators.pyc create mode 100644 gen_lexer.pyc create mode 100644 parser.pyc diff --git a/.chatlang.py.swp b/.chatlang.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..47ad3b5764b8f64ea211b4540b3df27fe36caaa8 GIT binary patch literal 12288 zcmeI2&uSAv9LIn4wn$X$3rtc%H_~nzdr(sH06hpn&`XWWG&|cYCOhlQG&V@}5yYE# zRqz3P1APK-fUBb9V)cE>( z*hq);>N1g?NF$Sp^re4|+P6KGh112k)@QY`TP)l}=iTu{x~a^M&K6OzIQKRem;e*F zFo9#2Zr^Uu?#fC(@GCcp%k025#W zOn?b6fz=@p{O7O>8(Ej=V;4O!nN#6r+xA7H8kIYwQ^wTkhTbS}piL7QVaigGYdoy%(5GOzK+pQ$>v4G!I{irH78jO_ zKMhlLqD(l}i9*>=DC$DxVVX^qf7ecAluSZY>k~M9RB4Xp2fYy?L>zl(9E-s~wBmRs fvpkMlz>^w1TgEe@B6DMTc$TmX@Vi*K@BsP+M$f#g literal 0 HcmV?d00001 diff --git a/.hello.cl.swp b/.hello.cl.swp new file mode 100644 index 0000000000000000000000000000000000000000..343a3c094ea7292dcc3bce2e9d2ab540a65e4689 GIT binary patch literal 12288 zcmeI%ze~eV5C`z9lNm(Of8f-mwWRq`tI}9!9fU5rKbyoC2uWz8;-ZVQ;NaroEV%eL z_&>Nf`$xEWsR@Em5jqvV502xwyT{|Rg`C*O2Zy}fYLlELqNn$V{(ab|PlITr)lm6b z6|KKcHD|^u%hPP)=v6gnWtX{56KkMV9;@Wk(m@=jtwB6-6%7bLU>bq5JT!L}sO>s- zan1VLDz7XbO>-l5ApijgKmY;|fB*y_0D&13$g>(<%bVxQch8r(_A8eq5(FRs0SG_< z0uX=z1Rwwb2tWV=Gbo^ZqWwjp8;RoofA#(UA@!TYtHg`Mv&5ssokTHvD}e(62tWV= z5P$##AOHafKmY;|fWZF+j4(=~k>+lX9g0FeS3{Tg*yb+%bZn1;#P`S;cV%>(o3e4C mqd533cYCtsr&P^!*yJs*D4pW=y2~3}-(-v3MuVH&@#qT);Ykz# literal 0 HcmV?d00001 diff --git a/ast_ds.py b/ast_ds.py index 22e8804..6905fee 100644 --- a/ast_ds.py +++ b/ast_ds.py @@ -9,17 +9,17 @@ def eval(self, env): return self.i class VarExp(ArithmeticExp): - def __init__(self, x): + def __init__(self, (x, _)): self.x = x def eval(self, env): - if self.name in env: - return env[self.name] - else + if self.x in env: + return env[self.x] + else: return 0 class BinopExp(ArithmeticExp): - def __init__(self, op, left, right): + def __init__(self, (op, _), left, right): self.op = op self.left = left self.right = right @@ -27,43 +27,43 @@ def __init__(self, op, left, right): def eval(self, env): left_val = self.left.eval(env) right_val = self.right.eval(env) - if self.op == '+' + if self.op == '+': return left_val + right_val - elif self.op == '-' + elif self.op == '-': return left_val - right_val - elif self.op == '*' + elif self.op == '*': return left_val * right_val - elif self.op == '/' + elif self.op == '/': return left_val / right_val - else - raise Excpetion('Unknown operator' + self.op) + else: + raise Exception('Unknown operator' + self.op) class BooleanExp: pass class RelExp(BooleanExp): - def __init__(self, op, left, right): + def __init__(self, (op, _), left, right): self.op = op - self.left = left - self.right = right + self.left = left + self.right = right def eval(self, env): left_val = self.left.eval(env) - right_val = self.right.eval(env) - if self.op == '<' - return left_val < right_val - elif self.op == '>' - return left_val > right_val - elif self.op == '<=' - return left_val <= right_val - elif self.op == '>=' - return left_val >= right_val - elif self.op == '==' + right_val = self.right.eval(env) + if self.op == '<': + return left_val < right_val + elif self.op == '>': + return left_val > right_val + elif self.op == '<=': + return left_val <= right_val + elif self.op == '>=': + return left_val >= right_val + elif self.op == '==': return left_val == right_val - elif self.op == '!=' + elif self.op == '!=': return left_val != right_val - else - raise Excpetion('Unknown operator' + self.op) + else: + raise Exception('Unknown operator' + self.op) class AndExp(BooleanExp): def __init__(self, left, right): @@ -78,26 +78,26 @@ def eval(self, env): class OrExp(BooleanExp): def __init__(self, left, right): self.left = left - self.right = right + self.right = right - def eval(self, env): - left_val = self.left.eval(env) - right_val = self.right.eval(env) - return left_val or right_val + def eval(self, env): + left_val = self.left.eval(env) + right_val = self.right.eval(env) + return left_val or right_val class NotExp(BooleanExp): def __init__(self, exp): - self.exp = exp + self.exp = exp - def eval(self, env): + def eval(self, env): val = self.exp.eval(env) - return not val + return not val class Statement: pass class AssignmentStatement(Statement): - def __init__(self, name, val): + def __init__(self, (name, _), val): self.name = name self.val = val @@ -116,7 +116,7 @@ def eval(self, env): self.body.eval(env) eval_cond = self.condition.eval(env) -class IfThenElseStatement(Statement): +class IfStatement(Statement): def __init__(self, condition, true_body, false_body): self.condition = condition self.true_body = true_body diff --git a/ast_ds.pyc b/ast_ds.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed41c79c676abded5615b127ad099908b306fdb1 GIT binary patch literal 7268 zcmd5>;cgR05T5hdPV6KE2yp@g2$Z%i0j&0K69l2CLX|?bR-r^luIszt1N+YS4hT{r zRi&+}zCfk^>udBa`n!+O7wCMmcYAi6pxBAqCfU8+*}I+DnQvxiH}>B%#p{3l_GN?O zADf@Ixr7yq$l<>WiQ8FCB4YTi(b z)pE3tXDd3 zqGsS%8qIpts{2nX^)RYA;myt)h0nk{1ed%O<@s^nkMl!ndPZ!SR36P6-zaQ)#}c}Q!h zWh=-j`a_ks3bvT>VZD1KGh#9bDGEEw4L0MG4DIrtO*@%)m~>k&Q*?+KZhou)SuuAH zT!&jER5i^dgj_ZiQmYyvukeVrDvSC&Pu+7d^w`d4tBlpE1B2uJ4Ua1l+ly>M6ss#> zBQ?ixLc)}@TtY}o)~q#^8IQ>eJpB|H8Iyzcy5|R-k&P}>@hnE4E8K~Vo~7L!?PO!4 z$JS@JW+bp@b0A1P322NkN6?8dWy{@)B4DBGJ#9v1fjqhj@+)XdokVp46)Zfe@-cIX zi^NbM1i#|@5CdWflz-)hM#(c?fJJXDU>cMRr4(7%Gv1OINg3*XDu}mfp^6uooDs5$ z$%_fu7P1$U&m`omkiUz`^Ho-i!}^q|7FdAqd7p#^v4jorlNG6NNPSJ}N(eQ5 z*Zy+Qal7>>=$1v5qD3GeRb>Q-+}m!royZIP)FTDo5-DMr8pkJjQ;dV&sM-hj^ISrh zfUMK}J8Mn$mNB^kc9?lEhLt(`e~tgI8)7Gaff)SG)lwK-CAk;-5S^v@5Q~Ui6$Drq3B4CIeYB zpxrS@26zXEzt9f(b25-K13B^3-;;ri8OY1PAIX4C;=~jCI5F0&op@Co_m)6mbxj_( z^kYpwF0T#e)Cxe~kgDLxiGSe>kz=WFaqHn z-x=9!5baTn*FZ@e?AiRBhnoFMg04;g@n*UkmGQC82U34w(kSZ1TJCX?)lu)BWTd;v z!-sN&3n3Tdh}j?g#{!T0kRJ=t@EA{0v&7SX21YRqvXVAD7(I>szoFnkvbZCo zp!1)gpbGiNje-lzWFt`&@8?-L&1?k!)}a)jako1?CxS~%88x;-TaU3C_pZv9o-gu#qDw($i{GkKDHF zM-A@C4`T5;mjmuWjwwgG%pAS}7pRtVv^jp<2}AFx4`JRR)nO7gSQkmTvG5h{#K$O` zcKHmY4^0PeLvn1wU5{GkPj!YbX}+A8KvVCbI_hDGcR%gUIzyAA5~N4Di!pH77n^z~ zPBjoIvB-z{(_6d6XY%h>*Sb1+*M}N0;?=n0vx#O z{jDRS2D70U)L5V~7|COW?xnMjk9OgQn3*TROrdZZ- z#g-Lj@ngaUv1KDwu5MwIlKIy1oQ(+nf sK6_>!>MB9*6<76trfwmZ3z0F+*`Cb?B8O_fgk14?DH1^@s6 literal 0 HcmV?d00001 diff --git a/chatlang.py b/chatlang.py new file mode 100755 index 0000000..35c4a95 --- /dev/null +++ b/chatlang.py @@ -0,0 +1,19 @@ +import sys +from chatlang_lexer import * +from parser import * + +if __name__ == "__main__": + filename = sys.argv[1] + code = open(filename).read() + tokens = chatlang_lexer(code) + parsed = chatlang_parse(tokens) + if not parsed: + sys.stderr.write('Parse error!\n') + sys.exit(1) + ast, pos = parsed + env = {} + #import pdb;pdb.set_trace(); + ast.eval(env) + + for name in env: + sys.stdout.write('%s: %s\n' % (name, env[name])) diff --git a/chatlang_lexer.py b/chatlang_lexer.py index d044b19..530c4e6 100644 --- a/chatlang_lexer.py +++ b/chatlang_lexer.py @@ -11,15 +11,15 @@ (r'\(', RESERVED), (r'\)', RESERVED), (r';', RESERVED), - (r':', RESERVED), + (r'\:', RESERVED), (r'\+', RESERVED), (r'-', RESERVED), (r'\*', RESERVED), (r'/', RESERVED), - (r'<', RESERVED), (r'<=', RESERVED), - (r'>', RESERVED), + (r'<', RESERVED), (r'>=', RESERVED), + (r'>', RESERVED), (r'==', RESERVED), (r'=', RESERVED), (r'!=', RESERVED), diff --git a/chatlang_lexer.pyc b/chatlang_lexer.pyc new file mode 100644 index 0000000000000000000000000000000000000000..359df8113894e7f36176423835caf95999a4632c GIT binary patch literal 1059 zcmbu6OLNmO5XaYcnAHl`9jpHNis_6g-{2j;vVgwgr2ZRT) z3t|Rh55yjbeGqdX9XLa0ouR=n&v1!hfnkwhiD8*xg<+Lpjo~uGI>QFTCc_nms|?o| zt~1kXJ6N;H5Bx%1M~r$Wz34^n zSbHd*g{Rsz>P zhAf1tVW5B5dVp8iF!;C?r>zfzixC6EL33oTV7j^BQ|xL5jRtt-z()iZKDk&vlM(tR zM>o)nWiia94m8t?Q2$4Sge`HDj=ujD ze!}tPyI=H&`A7T#`aI`mH%+7rAj>XZbhQtIi93IS#$D9a;9I&YW| z;-8UVNuD~g0VzrnEXyVoJWI;0&q#RnFd|{oTDdjqi zT0|GZoJQPg1!Ji==n|4WFY>oT&r7EDVY?OVHt}d@fh0PLuVA#Y)!GhM;w``92V4G1 z`$@9ZidJe{e$w=#&6Qef`(Y#UlU66b+J55lxAbaERt2NX+z{eD4uk(n_zEUj3E_OW zdIJ_QI>=FGu2JJ^Y<#$Z7MIcO8`*~z7&tnB(L|~hs=j8m%wtF$6aF^wH!J#0Y8*D} zE(h)ex}M6zYuhztBWfg`H_4^Bte7dIQqwmisML^^!X_&FQzMT=I^U`uj+{Nn$$n1m zJ93bhg9+KsQ>pJU2{>7oeNgb|q>q@ab~;mcq#8hP;)3eaL~9Kip+df@dNYiA$|fYQ zwW6>>*<5ytprrLEjAEVNZp8`hqNLt0kHJhezuEM>8LS)Q@>Km42|jq3qn;!0j3(M zy+_5H+T}DD>h6*r%BZADbDPo(pzQObP-bBTloh43N&pVPY7kT8;uhIa5qAn*4@WK| ziA(Rg^ln1^y^(jAf?x?tX)sG6Ge)?~g4ZeX<4yrwa=OQ>;m&Rt)xsKx`WXr{oSb6j z7RK@OFq07bZH?%$ZfM=5?nzL4n_>7+rO>%R=9Huh9r}`bDs(~&HboeUG!E^3s!>Pe z=7aY&C;={rx_RcNgM#cAq;s47eLPMwZ~;IFS^JZM=w^1x8j|pJ4;v1zK*VnuvAR*3 zj8!d-Kz_R&MuB3=hM(PbGps1sbn9g3(%Fyw=583PscO3&k1>n0tl4zf3FF;nqI=zs zlaVY(Z(T%-7to0*;BVf{p)Z+vr)b6q#(D5#uJ3`7&b#xBRR1EOYbY36z>(cC;%|kz z9Ks*NNQS?+@vwI}!`uZ$BA^4@IEpzSEH9mD_SKzfxfk?je3jS+B@IvguyI*6xM$Ff zjD>fwNK8CR7YAd-z*+FQN{%#C7Le|=lSV7@n=coT=z_HA-hW;|Qp+8RL^R|`He`Ui zhISYUR4#|6(p<)X1&>Xv*7+JkI?IeCEO`KlwD>tx^jRlsUgCllxmn~m9`ApMg|lQ- zF!^!TF5K1ckzeL}+U{=&x0)vyVFQ;)Qewx;E8H9yvOqZ$D@ zGA>rJqH5b$L{nfD_6rQ2jf@*a22&9VXgv}z6(LudG<7LpY@Mw&GeDGPA}(*Cv&_+v zbq%A3%VIU91F%QJ6PQ?sm!5`6;|iK>n6E&VU^co!I%DzPiho{pP}lT zG?esV3igGkJa_^B7~sS*U~$^HXZBu%N#^jvrgHDtfVGY_`+-HTv)$mEx6oN&#piTj zc{s_Mp^PZCnT0Iz01g-ys|^BqyC~Of)#kU=EM$IE{m1Gmr+6ZthXXYk^V3vPah- zSpXRs7C!iNYmfzmi>2`fjMu>J0XcS|k<+?*b+26mQ9nb)3!00qFJ5%R1iW&@jgHMh z3>tBW=)fDNfmGzaj+Y~umgyB`$3UbYm6}uL_z!f?1WxBtHwFEMLzE*C?i;b8enHB_ z<&=@1LCp(tD33$nVy}2`y9ssenF{Bt98Xqz@9N>|cfI!k_G|%UPh37t^d0M+P?)X0kznnfg-GCv|Af}@dN)zaXe1~W_%dr6iZr25l9FUvo^CCd(O$5x!ieEaHjNFSTKv@jMEpvw?PTAI9)Zsm#ntfm|tUsbB<7IRu ZXE9fvEl-!{%QN_!!>CkVVl8#j{0oOtJ@fzo literal 0 HcmV?d00001 diff --git a/gen_lexer.pyc b/gen_lexer.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff7dc4bf8925c082940a010a1ba0736b4ee17fd5 GIT binary patch literal 939 zcmbVK%}&%%6#j1KhXDp95{)cOh6QF46J~GJn5YXkgbWK3f=-(0wbP-~mU|C6B<%{H z!WZ!Yd^`MUc|-AS1EfF`LYtQAwZYpDJ2;8Zw9#}Rz}%p2h-EGcQ?V1@#Kt ziQ>GY4U?!@(@O3;>Y%V>vyM8#yo-8W155!NoSlHr5fl9{2-AC*uj=fw23g1UBMnr0 z9|>#m}Le{Cxmy-m2PM7zW5yHSu&W;Vi!-vy61ykKghT= z4DS&*I%l>l0!+(AmE~4+-MK9!FqVat4h@S~rn8`>t~{@#OIuu3wn##}zwp82C9%$h zxGydf%SlLlTil1(s`P=|E;9)Rk_`2u%I3ep6EDuji(r)U)D{zGQ@fnxnU_#27`^mv zk|xTlgH?f4;`EyDdaC3)+xoGN#qARANJGPcUbK-gtW73jml9kdbVv^Z_-E8cvvOfa zUS~^Y@u<3xSy_xYtK@N!j#4(9+og4T^ymh|ix&4!2{2vWd*&f=!}OyEJnvKbX4CX| pJ~o@tmI=T9SU=8KbH>|*yEgu>mmWD@EXw3Ox36?vUVG}Zg+BsQ$hQCh literal 0 HcmV?d00001 diff --git a/hello.cl b/hello.cl index 67d2f7e..07eee2d 100644 --- a/hello.cl +++ b/hello.cl @@ -1,6 +1,8 @@ -n := 5; -p := 1; -while n > 0 do - p := p * n; - n := n - 1 -end +var1 := (2 * (4 / 2)) + 5; +var2 := 5 - 6; +while var2 <= 0 do + var2 := var2 + 1 +end; +finish := 0; +if var2 > 0 : + finish := 1 diff --git a/parser.py b/parser.py index 1ab4023..daa0a4c 100644 --- a/parser.py +++ b/parser.py @@ -1,46 +1,55 @@ -from chatlang.lexer import * +from chatlang_lexer import * from ast_ds import * from combinators import * -arithmetic_precedence = [["*", "/"], ["+", "/"]] -boolean_precedence = ["and", "or"] +arithmetic_precedence = [["*", "/"], ["+", "-"]] +boolean_precedence = [["and"], ["or"]] def reserved_word(rw): return Reserved(rw, RESERVED) -num = Process(Tag(INT), (lambda i: int(i)) +num = Process(Tag(INT), (lambda (i, _) : int(i))) +id = Tag(ID) -var = Tag(ID) +def chatlang_parse(tokens): + ast_parser = parser() + #import pdb; pdb.set_trace(); + ast = ast_parser(tokens, 0) + return ast -def parser(tokens): +def parser(): return All(statements()) def statements(): separator = Process(reserved_word(";"), (lambda x: lambda f, s: CompoundStatement(f, s))) return Exp(single_stmt(), separator) -def stmt(): - return Or(if_statement(), while_statement(), assign_statement()) +def single_stmt(): + return Or(assign_statement(), if_statement(), while_statement()) def assign_statement(): def process(parsed): (name, _, exp) = parsed return AssignmentStatement(name, exp) - return Process(Sequence(var, reserved_word(":="), a_exp()), process) + return Process(Sequence(id, reserved_word(":="), a_exp()), process) def if_statement(): def process(parsed): - (_, condition, _, true_body, _, _, false_body) = parsed - return IfStatement(condition, true_body, false_body or None) + (_, condition, _, true_body, false_block) = parsed + if false_block: + (_, _, false_body) = false_block + else: + false_body = None + return IfStatement(condition, true_body, false_body) return Process(Sequence( reserved_word("if"), b_exp(), reserved_word(":"), - Lazy(statements()), - Optional( + Lazy(statements), + Optional(Sequence( reserved_word("else"), reserved_word(":"), - Lazy(statements()))), process) + Lazy(statements)))), process) def while_statement(): def process(parsed): @@ -50,24 +59,26 @@ def process(parsed): reserved_word("while"), b_exp(), reserved_word("do"), - Lazy(statements()), + Lazy(statements), reserved_word("end")), process) +#left (op) right + def a_exp(): def process(op): return lambda left, right : BinopExp(op, left, right) def operator_precedence(ops): - return Process(Or([reserved_word(op), op in ops]), process) + return Process(reduce(lambda l, r: Or(l, r), [reserved_word(op) for op in ops]), process) parser = Exp(a_exp_term(), operator_precedence(arithmetic_precedence[0])) for precedence_op in arithmetic_precedence[1:]: - parser = Exp(a_exp_term(), operator_precedence(precedence_op)) + parser = Exp(parser, operator_precedence(precedence_op)) return parser def a_exp_term(): return Or(a_exp_value(), a_exp_group()) def a_exp_value(): - return Or(Process(num, (lambda i: IntExp(i)), Process(var, (lambda a: VarExp(a))) + return Or(Process(num, (lambda i: IntExp(i))), Process(id, (lambda a: VarExp(a)))) def a_exp_group(): def process(parsed): @@ -78,4 +89,44 @@ def process(parsed): Lazy(a_exp), reserved_word(")")), process) +def b_exp(): + def process(op): + if op=="and": + return lambda left, right : AndExp(op, left, right) + elif op=="or": + return lambda left, right : OrExp(op, left, right) + else: + raise RuntimeError('unknown logic operator: ' + op) + def operator_precedence(ops): + return Process(reduce(lambda l, r: Or(l, r), [reserved_word(op) for op in ops]), process) + parser = Exp(b_exp_term(), operator_precedence(boolean_precedence[0])) + for precedence_op in boolean_precedence[1:]: + parser = Exp(parser, operator_precedence(precedence_op)) + return parser + +def b_exp_term(): + return Or(b_exp_not(), b_exp_relop(), b_exp_group()) + +def b_exp_not(): + def process(parsed): + (_, exp) = parsed + return NotExp(exp) + return Process(Sequence(reserved_word('not'), Lazy(b_exp_term)), process) +def b_exp_relop(): + def process(parsed): + (left, op, right) = parsed + return RelExp(op, left, right) + relops = ['<=', '<', '>=', '>', '==', '!='] + return Process(Sequence(a_exp(), + reduce(lambda l, r: Or(l, r), [reserved_word(op) for op in relops]), + a_exp()), process) + +def b_exp_group(): + def process(parsed): + (_, exp, _) = parsed + return exp + return Process(Sequence( + reserved_word('('), + Lazy(b_exp), + reserved_word(')')), process) diff --git a/parser.pyc b/parser.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3553dedef116c06fd72c3f2d59129435938a571c GIT binary patch literal 8039 zcmd5>e{&PZ8Qzm@!5NIf#$X51m`u_r$$&eVw4I^Y39+4qnO2Z9g&78_IqS}{#?qa* zI|Y-0{!w?@^egnM^<(q{w9oVGolbTjjbvsL*}HmoZ*TYA_kG?UyQ=F($yjW4gxN&nag}RX|ltP&IATQzn=uf&WKU6Vy!GF~KR;?7`F1=5NM4 znl@q0gr`h++8knl%g#d#aMd}(&PQiVFr&d|O>joUtO?GFIA?-c5pyOuC*r&b=0sdD z!Fdt$Cb%GC!36UnE}CFL#G(ln1;zWVPG3u+gsj)txdZy$0X0{sEw+W_3B>S3s=)UKk3}|s#HA61BoVdKob%7&8E??kZn1xuu@e2x< z`)PDg87smXC@T`nGuv6bA4X|51JX~krhRM`nP!u2;T&cwlwhZ1Ps;U-<5uRJ7Aby) zBF~`#(P2L$lI8%hbGzFWmuco_VK0m_0l;EGe2xVOZw~WbL@6);@+ek0C!`C8;0VAF zY~C8}OLPJfRWb^Z3&vFpZb z!Od?S^fO_+6SccxGtGLLg(^p+A<*b&aWW|viJM9R}=0MEOqO-F*(m`-N+v{jY`Ji$SqQ=wF+nA4j_&8Xn z(Wh;E48lT;=)}L1V-%LdqL=X9Y?1T(01%Dk8|$#vGb zrqMrbq8hUv5C9Lt2UPw7?3898PfTq#;wY5W-q|hH&V{}ASV_;c-&Pz2ovaf_ne06o zgw35ec&=A>{ceh8H*W3AtLJSzo>UoBo?LU3({E5xZXcSfmYS2!s^}$y{7!dJS)KjU zn13KjD}(qu3=D$7zw#VN-J1V06{T$R-~)9{nRhmyTq$!;jD#wNqn>_d9>Ohe%i&qe zr#GXC(l0B~&KGqI7CoMLP+J+Oc))WBGLD3R`$NMHxvzywASh9B*QJgiVwpY8Dcr?Q8R&fUvr-nrcf5n@{HH zFQFL9!!duZn5PTW4um8_;7%JY)-2TaHpMAIEvY=OOW?|CHXo`N)$@Y3-)@hB*V_%`Zl$-0sR<6>_p;cP(Sj>ALVcrdtl#y9y zkjho(g0t+j*ye|F(g^NzXrVyCVXk)Wt6{mMHWkK{NHo9ky8{~$ZM&VsgOL&}3xx~@B?9`}Dm{;IqH literal 0 HcmV?d00001