From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Panicz Maciej Godek Newsgroups: gmane.lisp.guile.user Subject: [potluck dish] Extremely simple Recursive Descent Parser Date: Tue, 16 Feb 2016 00:28:23 +0100 Message-ID: NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=001a1146978e78f6a7052bd75f10 X-Trace: ger.gmane.org 1455578922 1878 80.91.229.3 (15 Feb 2016 23:28:42 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Mon, 15 Feb 2016 23:28:42 +0000 (UTC) To: "guile-user@gnu.org" Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Tue Feb 16 00:28:41 2016 Return-path: Envelope-to: guile-user@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1aVSZN-00041K-HS for guile-user@m.gmane.org; Tue, 16 Feb 2016 00:28:41 +0100 Original-Received: from localhost ([::1]:37236 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVSZM-0003sm-VU for guile-user@m.gmane.org; Mon, 15 Feb 2016 18:28:40 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:33964) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVSZ8-0003lO-Mp for guile-user@gnu.org; Mon, 15 Feb 2016 18:28:28 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aVSZ6-0008Iu-Fu for guile-user@gnu.org; Mon, 15 Feb 2016 18:28:26 -0500 Original-Received: from mail-wm0-x22b.google.com ([2a00:1450:400c:c09::22b]:34103) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aVSZ6-0008Ie-4J for guile-user@gnu.org; Mon, 15 Feb 2016 18:28:24 -0500 Original-Received: by mail-wm0-x22b.google.com with SMTP id b205so86773823wmb.1 for ; Mon, 15 Feb 2016 15:28:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to:content-type; bh=+ivnCjFRFOlQ2vp3x9kJ2BjHDwjpG3TEMnGb5A1QGag=; b=bPTFQSAEh4ewYYQnGTA85APB0SLTMogqfRrsfx963JNCMVEKlt+CedjDRVwdeoEh6Q lap5PpnqaAzntpgzODuzBP72+8yIayiJ2buaepctwIKeSpdkO8T64TYY3nuulfUxooe9 sjWS2rCQlSenYRcg8Jvre9wav7MtD3fHNYot06ZthhZ4kLlS+b2fps34pOV0oz16TqnG ZJM3j1kGLhJVSVl4fFZclxR+o67+RFzk/NzE3zWnilbG884gh5aAj6KgG5GibpH092QM hYR5QLmxoKWLKIdLMfhSF6oXc1+CkQ3vE76ks/ItUaz9/oc/hJlPhIGrciGVXo9/YXcr yNGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to :content-type; bh=+ivnCjFRFOlQ2vp3x9kJ2BjHDwjpG3TEMnGb5A1QGag=; b=UTInreIeGY1C3+u6rf8E9blT914598LzwgwgPBH9/UwJI4oCph22dnd8xQsCaGYhmb 4jnbGsv8zHSBGHM8fwgSNVSS8ohyucKd5NC0iFC/WLYQ8F8xtzhVKVkpDNpsU+2i9bDV 4LTdCP/s/6SPkXVn70YoQabux3ZIx24p7KCQ+7rEHM7d1dIluNbcPU8qrUQL9aXeBSo3 YM8f7lRx9GG3p2bRwuCgAQCi/k7J7HRWSo1W6ZlrF8uFojTRzv0a33OL4k/t74z/LQP2 FH4ympsj6Cpia29h7JIpLoLnC37oWzHIfWInVWNNFz/tlZwwqmTEGvf4EXzCPJ8K39+K 2CtQ== X-Gm-Message-State: AG10YOQD0PK6mflsvIqpdMPBXDd2OMoVjsqfP748lE40o26EI+i9Pz5HBAq8Cy0I8m31lTsGHpjbrjdOLNT0NA== X-Received: by 10.28.111.91 with SMTP id k88mr14492147wmc.86.1455578903276; Mon, 15 Feb 2016 15:28:23 -0800 (PST) Original-Received: by 10.195.11.201 with HTTP; Mon, 15 Feb 2016 15:28:23 -0800 (PST) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::22b X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Original-Sender: guile-user-bounces+guile-user=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.user:12399 Archived-At: --001a1146978e78f6a7052bd75f10 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Sorry that I didn't have time to prepare anything better, but here's a little appetizer. It is my implementation of recursive descent parser. Perhaps it isn't as expressive as bison or yacc, but it is considerably shorter :) It implements backtracking through the mechanism of exceptions. The code is so small that I'm pasting it here, along with example usages. It uses the srfi-1 module as well as my (ice-9 nice-9) module to annoy Taylan a little ;] https://github.com/panicz/pamphlet/blob/master/libraries/ice-9/nice-9.scm Here it is: (use-modules (srfi srfi-1) (ice-9 nice-9)) (define ((in? l) x) (match l ((h . t) (or (eq? x h) ((in? t) x))) (_ #f))) (define (non-terminals grammar) (delete-duplicates (map (=CE=BB ((non-terminal =3D> . production)) non-terminal) grammar))) (define (terminals grammar) (let ((non-terminals (non-terminals grammar)) (productions (append-map (=CE=BB ((non-terminal =3D> . production)) production) grammar))) (delete-duplicates (lset-difference eq? productions non-terminals)))) (define ((recursive-descent-parser grammar) input) (let ((non-terminals (non-terminals grammar)) (terminals (terminals grammar)) (((initial-rule =3D> . _) . _) grammar)) (define (initial-match? rule input) (let* ((prefix _ (span (in? terminals) rule)) (prefix-length (length prefix))) (and (>=3D (length input) prefix-length) (equal? prefix (take input prefix-length))))) (define (match-rule rule input) (match rule (() (values '() input)) (((? (in? terminals) t) . next) (let (((token . input) input)) (if (eq? token t) (let ((rest input (match-rule next input))) (values `(,token . ,rest) input)) (throw 'parse-error input)))) (((? (in? non-terminals) A) . next) (let* ((parsed input (parse-rule A input)) (rest input (match-rule next input))) (values `(,parsed . ,rest) input))))) (define (parse-rule rule-name input) (let try ((variants (filter (=CE=BB ((name =3D> . rule)) (and (eq? name rule-name) (initial-match? rule input))) grammar))) (catch 'parse-error (=CE=BB () (match variants (((A =3D> . first-rule) . _) (let ((parsed input (match-rule first-rule input))) (values `(,A . ,parsed) input))) (_ (throw 'parse-error input)))) (=CE=BB errors (match variants ((failed . remaining) (try remaining)) (_ (throw 'parse-error input))))))) (parse-rule initial-rule input))) ;; for example: ((recursive-descent-parser '((A =3D> a A b) (A =3D> c))) '(a a a c b b b)) ;; =3D> (A a (A a (A a (A c) b) b) b) ((recursive-descent-parser '(( =3D> a d) ( =3D> ) ( =3D> b c) ( =3D> e))) '(a a b b e c c d d)) ; =3D> ( a ( a ( ( b ( b ( e) c) c)) d) d) ((recursive-descent-parser '(( =3D> ) ( =3D> a ) ( =3D> x) ( =3D> b ) ( =3D> x))) '(a a a a x b b x)) ; =3D> ( ( a ( a ( a ( a ( x))))) ( b ( b ( x)))) ;; note that the parser doesn''t always work; ;; it only works for the so-called LL grammars ;; for instance, the following grammar will result ;; in stack overflow: ((recursive-descent-parser '(( =3D> + ) ( =3D> ) ( =3D> a) ( =3D> c))) '(a + c + a + c + a + a)) ;; ~~~> ;; However, the grammar can be transformed to an equivalent form: ((recursive-descent-parser '(( =3D> +*) (+* =3D> + +*) (+* =3D> ) ( =3D> a) ( =3D> c))) '(a + c + a + c + a + a)) ;; but then, some strange non-terminal symbols appear: ;=3D> ( ( a) (+* + ( c) ; (+* + ( a) (+* + ( c) ; (+* + ( a) ; (+* + ( a) (+*))))))) ; We need to introduce additional transformation on the ouput: (define (eliminate+* tree) (match tree ((' +*) `( , . ,(eliminate+* +*))) (('+* '+ +*) `(+ , . ,(eliminate+* +*))) (('+*) '()) (_ tree))) (eliminate+* ((recursive-descent-parser '(( =3D> +*) (+* =3D> + +*) (+* =3D> ) ( =3D> a) ( =3D> c))) '(a + c + a + c + a + a))) ;=3D> ( ( a) + ( c) + ( a) + ( c) + ( a) + ( a)) Happy birthday, Guile! --001a1146978e78f6a7052bd75f10 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: base64 PGRpdiBkaXI9Imx0ciI+PGRpdj48ZGl2PjxkaXY+PGRpdj48ZGl2PjwvZGl2PlNvcnJ5IHRoYXQg SSBkaWRuJiMzOTt0IGhhdmUgdGltZSB0byBwcmVwYXJlIGFueXRoaW5nIGJldHRlciwgYnV0IGhl cmUmIzM5O3MgYSBsaXR0bGUgYXBwZXRpemVyLjxicj48L2Rpdj5JdCBpcyBteSBpbXBsZW1lbnRh dGlvbiBvZiByZWN1cnNpdmUgZGVzY2VudCBwYXJzZXIuIFBlcmhhcHMgaXQgaXNuJiMzOTt0IGFz IGV4cHJlc3NpdmUgYXMgYmlzb24gb3IgeWFjYywgYnV0IGl0IGlzIGNvbnNpZGVyYWJseSBzaG9y dGVyIDopPGJyPjxicj48L2Rpdj5JdCBpbXBsZW1lbnRzIGJhY2t0cmFja2luZyB0aHJvdWdoIHRo ZSBtZWNoYW5pc20gb2YgZXhjZXB0aW9ucy4gVGhlIGNvZGUgaXMgc28gc21hbGwgdGhhdCBJJiMz OTttIHBhc3RpbmcgaXQgaGVyZSwgYWxvbmcgd2l0aCBleGFtcGxlIHVzYWdlcy4gSXQgdXNlcyB0 aGUgc3JmaS0xIG1vZHVsZSBhcyB3ZWxsIGFzIG15IChpY2UtOSBuaWNlLTkpIG1vZHVsZSB0byBh bm5veSBUYXlsYW4gYSBsaXR0bGUgO108YnI+PGEgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL3Bh bmljei9wYW1waGxldC9ibG9iL21hc3Rlci9saWJyYXJpZXMvaWNlLTkvbmljZS05LnNjbSI+aHR0 cHM6Ly9naXRodWIuY29tL3Bhbmljei9wYW1waGxldC9ibG9iL21hc3Rlci9saWJyYXJpZXMvaWNl LTkvbmljZS05LnNjbTwvYT48YnI+PGJyPjwvZGl2PkhlcmUgaXQgaXM6PGJyPjxicj48L2Rpdj4o dXNlLW1vZHVsZXMgKHNyZmkgc3JmaS0xKSAoaWNlLTkgbmljZS05KSk8YnI+PGRpdj48YnI+KGRl ZmluZSAoKGluPyBsKSB4KTxicj7CoCAobWF0Y2ggbDxicj7CoMKgwqAgKChoIC4gdCk8YnI+wqDC oMKgwqAgKG9yIChlcT8geCBoKTxicj7CoMKgwqDCoMKgwqDCoMKgICgoaW4/IHQpIHgpKSk8YnI+ wqDCoMKgIChfPGJyPsKgwqDCoMKgICNmKSkpPGJyPjxicj4oZGVmaW5lIChub24tdGVybWluYWxz IGdyYW1tYXIpPGJyPsKgIChkZWxldGUtZHVwbGljYXRlczxicj7CoMKgIChtYXAgKM67ICgobm9u LXRlcm1pbmFsID0mZ3Q7IC4gcHJvZHVjdGlvbikpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoCBub24t dGVybWluYWwpPGJyPsKgwqDCoMKgwqDCoMKgIGdyYW1tYXIpKSk8YnI+PGJyPihkZWZpbmUgKHRl cm1pbmFscyBncmFtbWFyKTxicj7CoCAobGV0ICgobm9uLXRlcm1pbmFscyAobm9uLXRlcm1pbmFs cyBncmFtbWFyKSk8YnI+wqDCoMKgwqDCoMKgwqAgKHByb2R1Y3Rpb25zIChhcHBlbmQtbWFwICjO uyAoKG5vbi10ZXJtaW5hbCA9Jmd0OyAuIHByb2R1Y3Rpb24pKTxicj7CoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBwcm9k dWN0aW9uKTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGdyYW1tYXIpKSk8YnI+wqDCoMKgIChkZWxldGUtZHVwbGljYXRl cyAobHNldC1kaWZmZXJlbmNlIGVxPyBwcm9kdWN0aW9ucyBub24tdGVybWluYWxzKSkpKTxicj48 YnI+KGRlZmluZSAoKHJlY3Vyc2l2ZS1kZXNjZW50LXBhcnNlciBncmFtbWFyKSBpbnB1dCk8YnI+ wqAgKGxldCAoKG5vbi10ZXJtaW5hbHMgKG5vbi10ZXJtaW5hbHMgZ3JhbW1hcikpPGJyPsKgwqDC oMKgwqDCoMKgICh0ZXJtaW5hbHMgKHRlcm1pbmFscyBncmFtbWFyKSk8YnI+wqDCoMKgwqDCoMKg wqAgKCgoaW5pdGlhbC1ydWxlID0mZ3Q7IC4gXykgLiBfKSBncmFtbWFyKSk8YnI+wqDCoMKgIChk ZWZpbmUgKGluaXRpYWwtbWF0Y2g/IHJ1bGUgaW5wdXQpPGJyPsKgwqDCoMKgwqAgKGxldCogKChw cmVmaXggXyAoc3BhbiAoaW4/IHRlcm1pbmFscykgcnVsZSkpPGJyPsKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCAocHJlZml4LWxlbmd0aCAobGVuZ3RoIHByZWZpeCkpKTxicj7CoMKgwqDCoMKgwqDC oCAoYW5kICgmZ3Q7PSAobGVuZ3RoIGlucHV0KSBwcmVmaXgtbGVuZ3RoKTxicj7CoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgKGVxdWFsPyBwcmVmaXggKHRha2UgaW5wdXQgcHJlZml4LWxlbmd0aCkp KSkpPGJyPjxicj7CoMKgwqAgKGRlZmluZSAobWF0Y2gtcnVsZSBydWxlIGlucHV0KTxicj7CoMKg wqDCoMKgIChtYXRjaCBydWxlPGJyPsKgwqDCoMKgwqDCoMKgICgoKTxicj7CoMKgwqDCoMKgwqDC oMKgICh2YWx1ZXM8YnI+wqDCoMKgwqDCoMKgwqDCoMKgICYjMzk7KCk8YnI+wqDCoMKgwqDCoMKg wqDCoMKgIGlucHV0KSk8YnI+PGJyPsKgwqDCoMKgwqDCoMKgICgoKD8gKGluPyB0ZXJtaW5hbHMp IHQpIC4gbmV4dCk8YnI+wqDCoMKgwqDCoMKgwqDCoCAobGV0ICgoKHRva2VuIC4gaW5wdXQpIGlu cHV0KSk8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqAgKGlmIChlcT8gdG9rZW4gdCk8YnI+wqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAobGV0ICgocmVzdCBpbnB1dCAobWF0Y2gtcnVsZSBuZXh0 IGlucHV0KSkpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICh2YWx1ZXM8YnI+ wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBgKCx0b2tlbiAuICxyZXN0KTxicj7C oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlucHV0KSk8YnI+wqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCAodGhyb3cgJiMzOTtwYXJzZS1lcnJvciBpbnB1dCkpKSk8YnI+PGJy PsKgwqDCoMKgwqDCoMKgICgoKD8gKGluPyBub24tdGVybWluYWxzKSBBKSAuIG5leHQpPGJyPsKg wqDCoMKgwqDCoMKgwqAgKGxldCogKChwYXJzZWQgaW5wdXQgKHBhcnNlLXJ1bGUgQSBpbnB1dCkp PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAocmVzdCBpbnB1dCAobWF0Y2gtcnVs ZSBuZXh0IGlucHV0KSkpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgICh2YWx1ZXM8YnI+wqDCoMKg wqDCoMKgwqDCoMKgwqDCoCBgKCxwYXJzZWQgLiAscmVzdCk8YnI+wqDCoMKgwqDCoMKgwqDCoMKg wqDCoCBpbnB1dCkpKSkpPGJyPjxkaXY+PGRpdj48YnI+wqDCoMKgIChkZWZpbmUgKHBhcnNlLXJ1 bGUgcnVsZS1uYW1lIGlucHV0KTxicj7CoMKgwqDCoMKgIChsZXQgdHJ5ICgodmFyaWFudHMgKGZp bHRlciAozrsgKChuYW1lID0mZ3Q7IC4gcnVsZSkpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKGFuZCAoZXE/ IG5hbWUgcnVsZS1uYW1lKTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAoaW5pdGlhbC1tYXRj aD8gcnVsZSBpbnB1dCkpKTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZ3JhbW1hcikpKTxicj7CoMKgwqDCoMKgwqDC oCAoY2F0Y2ggJiMzOTtwYXJzZS1lcnJvcjxicj7CoMKgwqDCoMKgwqDCoMKgwqAgKM67ICgpPGJy PsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKG1hdGNoIHZhcmlhbnRzPGJyPsKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgICgoKEEgPSZndDsgLiBmaXJzdC1ydWxlKSAuIF8pPGJyPsKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgKGxldCAoKHBhcnNlZCBpbnB1dCAobWF0Y2gtcnVsZSBmaXJzdC1y dWxlIGlucHV0KSkpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICh2YWx1ZXM8 YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBgKCxBIC4gLHBhcnNlZCk8YnI+ wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpbnB1dCkpKTxicj7CoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCAoXzxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICh0aHJv dyAmIzM5O3BhcnNlLWVycm9yIGlucHV0KSkpKTxicj7CoMKgwqDCoMKgwqDCoMKgwqAgKM67IGVy cm9yczxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgIChtYXRjaCB2YXJpYW50czxicj7CoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoCAoKGZhaWxlZCAuIHJlbWFpbmluZyk8YnI+wqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCAodHJ5IHJlbWFpbmluZykpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIChfPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKHRocm93ICYjMzk7cGFy c2UtZXJyb3IgaW5wdXQpKSkpKSkpPGJyPsKgwqDCoCAocGFyc2UtcnVsZSBpbml0aWFsLXJ1bGUg aW5wdXQpKSk8YnI+PGJyPjwvZGl2PjxkaXY+OzsgZm9yIGV4YW1wbGU6PGJyPjwvZGl2PjxkaXY+ KChyZWN1cnNpdmUtZGVzY2VudC1wYXJzZXIgJiMzOTsoKEEgPSZndDsgYSBBIGIpPGJyPsKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIChBID0m Z3Q7IGMpKSk8YnI+wqAmIzM5OyhhIGEgYSBjIGIgYiBiKSk8YnI+OzsgPSZndDsgKEEgYSAoQSBh IChBIGEgKEEgYykgYikgYikgYik8YnI+PGJyPigocmVjdXJzaXZlLWRlc2NlbnQtcGFyc2VyICYj Mzk7KCgmbHQ7UyZndDsgPSZndDsgYSAmbHQ7UyZndDsgZCk8YnI+wqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZsdDtTJmd0OyA9Jmd0OyAm bHQ7QiZndDspPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgICgmbHQ7QiZndDsgPSZndDsgYiAmbHQ7QiZndDsgYyk8YnI+wqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZsdDtCJmd0 OyA9Jmd0OyBlKSkpPGJyPsKgJiMzOTsoYSBhIGIgYiBlIGMgYyBkIGQpKTxicj47ID0mZ3Q7ICgm bHQ7UyZndDsgYSAoJmx0O1MmZ3Q7IGEgKCZsdDtTJmd0OyAoJmx0O0ImZ3Q7IGIgKCZsdDtCJmd0 OyBiICgmbHQ7QiZndDsgZSkgYykgYykpIGQpIGQpPGJyPjxicj4oKHJlY3Vyc2l2ZS1kZXNjZW50 LXBhcnNlciAmIzM5OygoJmx0O1MmZ3Q7ID0mZ3Q7ICZsdDtBJmd0OyAmbHQ7QiZndDspPGJyPsKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICgm bHQ7QSZndDsgPSZndDsgYSAmbHQ7QSZndDspPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICgmbHQ7QSZndDsgPSZndDsgeCk8YnI+wqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZs dDtCJmd0OyA9Jmd0OyBiICZsdDtCJmd0Oyk8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZsdDtCJmd0OyA9Jmd0OyB4KSkpPGJyPsKg JiMzOTsoYSBhIGEgYSB4IGIgYiB4KSk8YnI+OyA9Jmd0OyAoJmx0O1MmZ3Q7ICgmbHQ7QSZndDsg YSAoJmx0O0EmZ3Q7IGEgKCZsdDtBJmd0OyBhICgmbHQ7QSZndDsgYSAoJmx0O0EmZ3Q7IHgpKSkp KSAoJmx0O0ImZ3Q7IGIgKCZsdDtCJmd0OyBiICgmbHQ7QiZndDsgeCkpKSnCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCA8YnI+PGJyPjwvZGl2PjxkaXY+Ozsgbm90ZSB0aGF0IHRoZSBwYXJzZXIgZG9lc24mIzM5 OyYjMzk7dCBhbHdheXMgd29yazs8YnI+OzsgaXQgb25seSB3b3JrcyBmb3IgdGhlIHNvLWNhbGxl ZCBMTCBncmFtbWFyczxicj48L2Rpdj48ZGl2Pjs7IGZvciBpbnN0YW5jZSwgdGhlIGZvbGxvd2lu ZyBncmFtbWFyIHdpbGwgcmVzdWx0PGJyPjwvZGl2PjxkaXY+OzsgaW4gc3RhY2sgb3ZlcmZsb3c6 PGJyPjxicj4oKHJlY3Vyc2l2ZS1kZXNjZW50LXBhcnNlciAmIzM5OygoJmx0O0EmZ3Q7ID0mZ3Q7 ICZsdDtBJmd0OyArICZsdDtCJmd0Oyk8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZsdDtBJmd0OyA9Jmd0OyAmbHQ7QiZndDspPGJy PsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg ICgmbHQ7QiZndDsgPSZndDsgYSk8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZsdDtCJmd0OyA9Jmd0OyBjKSkpPGJyPsKgJiMzOTso YSArIGMgKyBhICsgYyArIGEgKyBhKSk8YnI+PGJyPjwvZGl2PjxkaXY+Ozsgfn5+Jmd0OyAmbHQ7 Ym9vbSEmZ3Q7PGJyPjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+OzsgSG93ZXZlciwgdGhlIGdy YW1tYXIgY2FuIGJlIHRyYW5zZm9ybWVkIHRvIGFuIGVxdWl2YWxlbnQgZm9ybTo8YnI+PGJyPigo cmVjdXJzaXZlLWRlc2NlbnQtcGFyc2VyICYjMzk7KCgmbHQ7QSZndDsgPSZndDsgJmx0O0ImZ3Q7 ICsmbHQ7QiZndDsqKTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCAoKyZsdDtCJmd0OyogPSZndDsgKyAmbHQ7QiZndDsgKyZsdDtCJmd0 OyopPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgICgrJmx0O0ImZ3Q7KiA9Jmd0OyApPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICgmbHQ7QiZndDsgPSZndDsgYSk8YnI+wqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKCZs dDtCJmd0OyA9Jmd0OyBjKSkpPGJyPsKgJiMzOTsoYSArIGMgKyBhICsgYyArIGEgKyBhKSk8YnI+ PGJyPjwvZGl2PjxkaXY+OzsgYnV0IHRoZW4sIHNvbWUgc3RyYW5nZSBub24tdGVybWluYWwgc3lt Ym9scyBhcHBlYXI6PGJyPjs9Jmd0OyAoJmx0O0EmZ3Q7ICgmbHQ7QiZndDsgYSkgKCsmbHQ7QiZn dDsqICsgKCZsdDtCJmd0OyBjKTxicj47wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICgrJmx0O0Im Z3Q7KiArICgmbHQ7QiZndDsgYSkgKCsmbHQ7QiZndDsqICsgKCZsdDtCJmd0OyBjKTxicj47wqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgICgrJmx0O0ImZ3Q7KiArICgmbHQ7QiZndDsgYSk8YnI+O8Kg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAoKyZsdDtCJmd0OyogKyAoJmx0O0ImZ3Q7IGEpICgrJmx0 O0ImZ3Q7KikpKSkpKSk8YnI+PGJyPjwvZGl2PjxkaXY+OyBXZSBuZWVkIHRvIGludHJvZHVjZSBh ZGRpdGlvbmFsIHRyYW5zZm9ybWF0aW9uIG9uIHRoZSBvdXB1dDo8YnI+PGJyPihkZWZpbmUgKGVs aW1pbmF0ZSsmbHQ7QiZndDsqIHRyZWUpPGJyPsKgIChtYXRjaCB0cmVlPGJyPsKgwqDCoCAoKCYj Mzk7Jmx0O0EmZ3Q7ICZsdDtCJmd0OyArJmx0O0ImZ3Q7Kik8YnI+wqDCoMKgwqAgYCgmbHQ7QSZn dDsgLCZsdDtCJmd0OyAuICwoZWxpbWluYXRlKyZsdDtCJmd0OyogKyZsdDtCJmd0OyopKSk8YnI+ PGJyPsKgwqDCoCAoKCYjMzk7KyZsdDtCJmd0OyogJiMzOTsrICZsdDtCJmd0OyArJmx0O0ImZ3Q7 Kik8YnI+wqDCoMKgwqAgYCgrICwmbHQ7QiZndDsgLiAsKGVsaW1pbmF0ZSsmbHQ7QiZndDsqICsm bHQ7QiZndDsqKSkpPGJyPjxicj7CoMKgwqAgKCgmIzM5OysmbHQ7QiZndDsqKTxicj7CoMKgwqDC oCAmIzM5OygpKTxicj48YnI+wqDCoMKgIChfPGJyPsKgwqDCoMKgIHRyZWUpKSk8YnI+PGJyPihl bGltaW5hdGUrJmx0O0ImZ3Q7Kjxicj7CoCgocmVjdXJzaXZlLWRlc2NlbnQtcGFyc2VyICYjMzk7 KCgmbHQ7QSZndDsgPSZndDsgJmx0O0ImZ3Q7ICsmbHQ7QiZndDsqKTxicj7CoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICgrJmx0O0ImZ3Q7 KiA9Jmd0OyArICZsdDtCJmd0OyArJmx0O0ImZ3Q7Kik8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAoKyZsdDtCJmd0OyogPSZndDsg KTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgICgmbHQ7QiZndDsgPSZndDsgYSk8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAoJmx0O0ImZ3Q7ID0mZ3Q7IGMpKSk8YnI+ wqAgJiMzOTsoYSArIGMgKyBhICsgYyArIGEgKyBhKSkpPGJyPjxicj47PSZndDsgKCZsdDtBJmd0 OyAoJmx0O0ImZ3Q7IGEpICsgKCZsdDtCJmd0OyBjKSArICgmbHQ7QiZndDsgYSkgKyAoJmx0O0Im Z3Q7IGMpICsgKCZsdDtCJmd0OyBhKSArICgmbHQ7QiZndDsgYSkpwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCA8YnI+PGJyPjwvZGl2PkhhcHB5IGJpcnRoZGF5LCBHdWlsZSE8YnI+PGJy PjwvZGl2PjwvZGl2PjwvZGl2Pg0K --001a1146978e78f6a7052bd75f10--