From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: =?UTF-8?Q?Marc_Nieper=2DWi=C3=9Fkirchen?= Newsgroups: gmane.lisp.guile.devel Subject: Re: Feature request: Expose `ellipsis?' from psyntax.ss Date: Sat, 24 Nov 2018 10:08:55 +0100 Message-ID: References: <875zwzmq4n.fsf@netris.org> <87pnv6iss7.fsf@netris.org> <87k1leip2i.fsf@netris.org> <124085ab-2a76-4892-e790-d58b07bcb3fc@nieper-wisskirchen.de> <87lg5tj3gn.fsf@netris.org> <874lcgzj9p.fsf@netris.org> <871s7fuml7.fsf@netris.org> <87a7lzpmly.fsf@netris.org> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="000000000000b1b244057b6573e2" X-Trace: blaine.gmane.org 1543051410 3528 195.159.176.226 (24 Nov 2018 09:23:30 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 24 Nov 2018 09:23:30 +0000 (UTC) Cc: guile-devel@gnu.org To: mhw@netris.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Sat Nov 24 10:23:25 2018 Return-path: Envelope-to: guile-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gQU9q-0000jX-5J for guile-devel@m.gmane.org; Sat, 24 Nov 2018 10:23:23 +0100 Original-Received: from localhost ([::1]:55856 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gQUBw-0007zv-LB for guile-devel@m.gmane.org; Sat, 24 Nov 2018 04:25:32 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:59943) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gQUAA-0006OF-Qm for guile-devel@gnu.org; Sat, 24 Nov 2018 04:23:46 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gQTw6-0003eK-Fy for guile-devel@gnu.org; Sat, 24 Nov 2018 04:09:14 -0500 Original-Received: from mail-pf1-f179.google.com ([209.85.210.179]:39660) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gQTw4-0003b5-PV for guile-devel@gnu.org; Sat, 24 Nov 2018 04:09:09 -0500 Original-Received: by mail-pf1-f179.google.com with SMTP id c72so4310242pfc.6 for ; Sat, 24 Nov 2018 01:09:07 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=VEd60TDYcRPPL+0tm70Sn6YmlYNahYelFcnCyb1PUFw=; b=csL0PXT38hJ+DbxAEdYO3v1n8/x/44Y6Zm9CI+/c5G+c/W9Y7Sg/yWmmGrGaDwZmEi NXtwGnf+pcH1PsbwRIk2xXQzgeZXFdw6iuCRNe/gBhbyLdyHWzULVfX8AHQBMzda7Xj2 7EZDcvqXU+JO1uKKmoIi5kahNrrvjN7lPPN97Qlp/yu+h2o48eIpLbwsxU6Q9mMpsHfQ AZXyruUzpvbwBxCwViiMMSRIRe7RWTX5Jt8+G5q1TZGA40zsxAjiYEj0CbSdB6hMeGmW Xn32JFkynAGVVyLM8IOOB4A6xF6K1OAfwc9Sn3qAb4YFp1/KZTchPIP+OrMIlShSqRJ8 XoHg== X-Gm-Message-State: AA+aEWbj2ZH2vkvkwFo8MmUkrn6WFk/A767o7m2NNuwM60RXbyAUPJaB oHnnyXjyVKfFvTj6XIIJzQ8DITvsHaZ98UusbcY= X-Google-Smtp-Source: AFSGD/W0F+pxyR0q4AOe0La2zpAww6oQycBxEUYiiW3plW3uFi7Zkq3NMaf04ndDQoVN1PI03lBEM40LSaF/8uGYNBQ= X-Received: by 2002:a63:f615:: with SMTP id m21mr17660432pgh.428.1543050546490; Sat, 24 Nov 2018 01:09:06 -0800 (PST) In-Reply-To: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.85.210.179 X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Original-Sender: "guile-devel" Xref: news.gmane.org gmane.lisp.guile.devel:19764 Archived-At: --000000000000b1b244057b6573e2 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Am Fr., 23. Nov. 2018 um 22:28 Uhr schrieb Marc Nieper-Wi=C3=9Fkirchen < marc@nieper-wisskirchen.de>: > Hi Mark, > > Am Fr., 23. Nov. 2018 um 21:26 Uhr schrieb Mark H Weaver = : > >> Hi Marc, >> >> Marc Nieper-Wi=C3=9Fkirchen writes: >> >> > Am Mi., 21. Nov. 2018 um 04:38 Uhr schrieb Mark H Weaver < >> mhw@netris.org>: >> > >> > I'm not aware of any language in the R[567]RS that makes it clear >> > whether '...' should be recognized as an ellipsis if it is bound to a >> > variable. The Scheme implementations I tried do not seem to agree. >> > >> > For example, consider this example: >> > >> > (let ((... 'hello)) >> > (let-syntax ((foo (syntax-rules () >> > ((foo x ...) >> > '((x) ...))))) >> > (foo 1 2))) >> > >> > If '...' is recognized as an ellipsis within the 'let', then the resu= lt >> > will be '((1) (2)). Otherwise, the result will be '((1) 2). >> > >> > I just tested with Chez Scheme 9.5 as well. It returns the same result >> as Chibi, namely '((1) 2). >> > >> > I found that Racket 7.0, Chicken 4.13.0, and Scheme48 1.9.2 return >> > '((1) (2)). Chibi-Scheme returns '((1) 2). I see the same results >> > with this variant: >> > >> > (let-syntax ((... (syntax-rules ()))) >> > (let-syntax ((foo (syntax-rules () >> > ((foo x ...) >> > '((x) ...))))) >> > (foo 1 2))) >> > >> > Again, Chez returns '((1) 2). >> > >> > If we instead bind '...' as a top-level variable: >> > >> > (define-syntax ... (syntax-rules ())) >> > (let-syntax ((foo (syntax-rules () >> > ((foo x ...) >> > '((x) ...))))) >> > (foo 1 2)) >> > >> > Then all four of these Schemes agree that the answer is '((1) (2)), >> > including Chibi-Scheme. >> > >> > Chez Scheme still returns '((1) 2) and thus does not recognize `...' >> > as the ellipsis. >> >> I stand by the analysis in my previous response in this thread, but >> nonetheless I've since realized that given the current implementation of >> 'ellipsis?' in psyntax.scm, Guile *should* be returning '((1) 2) in >> these examples above, and I'm not sure why that's not happening. >> >> The reason is that when no ellipsis binding is present, Guile currently >> uses 'free-identifier=3D?' to compare identifiers with #'(... ...) in >> psyntax.scm, where '...' has no binding. In the examples above, the >> ellipsis identifiers are within a lexical environment where '...' is >> bound, so 'free-identifier=3D?' should be returning #false in these case= s. >> >> Having said this, given the analysis in my previous response, I'm now >> wondering whether it's a mistake to use 'free-identifier=3D?' to check f= or >> the default ellipsis '...'. > > > Using `free-identifier=3D?' to check for the default ellipsis is definite= ly > correct. `bound-identifier=3D?' would not give the correct results in R[6= 7]RS > because the ellipsis can be imported under several names. Or use a macro > that produces aliases of the ellipsis: > > (import (scheme base)) > > (define-syntax foo > (syntax-rules () > ((foo e) TEMPLATE))) > > (foo ...) > > In TEMPLATE, both `...' and `e' have to be the ellipsis. They are > `free-identifier=3D?' but not `bound-identifier=3D?'. > > >> Within the lexical scope of >> 'with-ellipsis', Guile uses 'bound-identifier=3D?' to check for the >> user-defined ellipsis, but perhaps we should be using it uniformly to >> check for ellipsis in all cases. >> > > Using `free-identifier=3D?' would only make sense in a model, in which > `(with-ellipsis E BODY)' actually binds `E', wouldn't it? In any case, > inside the scope of `with-ellipsis', the behavior of `syntax-case', > `syntax' differs substantially with respect to the ellipsis from the > behavior outside of `with-ellipsis'. Thus it coud very well be that we ne= ed > `bound-identifier=3D?' in scopes inside `with-ellipsis' and > `free-identifier=3D?' in scopes outside. > P.S.: Thought about it a bit more. I think it would work if we used `free-identifier=3D?' everywhere, which means: `(with-ellipsis E BODY)' stores the current binding of `E' in CURRENT-ELLIPSIS-BINDING. In body, `(ellipsis-identifier? x)' then compares the binding of `x' with CURRENT-ELLIPSIS-BINDING. This should be compatible with SRFI-46/R7RS: `(syntax-rules ::: ( ---) ---)' would be rewritten as `(let-syntax ((::: (auxiliary-syntax))) (with-ellpsis ::: (syntax-rules ( ...) ---)'. Does this make sense? -- Marc > >> What do you think? >> >> Also, for the sake of completeness I should explain a remaining detail >> of Guile's semantics for 'with-ellipsis' bindings. In my previous >> response, I wrote: >> >> > I think it makes more sense to model (with-ellipsis E BODY) as >> > introducing a new lexical binding with a fixed name. CURRENT-ELLIPSI= S >> > would be a good name if we wanted to make it part of the public API. >> In >> > this model, 'E' becomes the _value_ of the binding, instead of the >> > binding's name. >> >> In existing versions of Guile, the special fixed name is equivalent to: >> >> (datum->syntax E '#{ $sc-ellipsis }#) >> >> Where E is the identifier being bound or checked. In particular, the >> _marks_ from E are copied to the special fixed name, both when binding a >> new ellipsis identifier and when checking for ellipsis identifier. In >> other words, for each set of marks there's a distinct ellipsis binding. >> > > That is actually good for hygiene as the following two examples (from an > earlier post in this thread) show: > > So this one indeed outputs #f, thus reproducing your result. > > (eval-when (expand) > (define-syntax bar2 > (syntax-rules () > ((_ e body) > (with-ellipsis e body))))) > > (define-syntax foo2 > (lambda (stx) > (bar2 e (syntax-case stx () > ((_a ...) > #'#t) > ((_ a b c) > #'#f))))) > > (display (foo2 1 2 3)) > (newline) > > On the other hand, this one prints #t. > > (eval-when (expand) > (define-syntax bar2 > (syntax-rules () > ((_ e body) > (with-ellipsis f body))))) ; THE DIFFERENCE IS HERE. > > (define-syntax foo2 > (lambda (stx) > (bar2 e (syntax-case stx () > ((_a ...) > #'#t) > ((_ a b c) > #'#f))))) > > (display (foo2 1 2 3)) > (newline) > > > >> WARNING: Note that the name #{ $sc-ellipsis }# is not part of Guile's >> API, and is subject to change. We reserve the right to change its name, >> or to make it completely inaccessible in future versions of Guile. > > > I think this would be a good move. Internal names shouldn't be forgeable > by user code. One may think that no one would name their identifiers `{# > $sc-ellipsis }#', but these kind of issues can and do happen in > meta-compiling, code reflection, etc. > > >> For example, we might move the ellipsis bindings out of the substitution >> and >> into a separate field in the wrap which maps marks to ellipsis >> identifiers, with no name involved at all. >> > > Such an approach could be generalized to an API that allows to attach > custom properties to a lexical scope. > > >> I welcome input on these questions. >> > > Anytime, although I should mention that I am not an expert. > > -- Marc > > --=20 Prof. Dr. Marc Nieper-Wi=C3=9Fkirchen Universit=C3=A4t Augsburg Institut f=C3=BCr Mathematik Universit=C3=A4tsstra=C3=9Fe 14 86159 Augsburg Tel: 0821/598-2146 Fax: 0821/598-2090 E-Mail: marc.nieper-wisskirchen@math.uni-augsburg.de Web: www.math.uni-augsburg.de/alg/mitarbeiter/mnieper/ --000000000000b1b244057b6573e2 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Am Fr., 23. No= v. 2018 um 22:28=C2=A0Uhr schrieb Marc Nieper-Wi=C3=9Fkirchen <marc@nieper-wisskirchen.de>:<= br>
Hi Mark,

Am Fr., 23. Nov. 2018 um 21:26=C2= =A0Uhr schrieb Mark H Weaver <mhw@netris.org>:
Hi Marc,

Marc Nieper-Wi=C3=9Fkirchen <marc@nieper-wisskirchen.de> writes:

> Am Mi., 21. Nov. 2018 um 04:38 Uhr schrieb Mark H Weaver <mhw@netris.org>:
>
>=C2=A0 I'm not aware of any language in the R[567]RS that makes it = clear
>=C2=A0 whether '...' should be recognized as an ellipsis if it = is bound to a
>=C2=A0 variable.=C2=A0 The Scheme implementations I tried do not seem t= o agree.
>
>=C2=A0 For example, consider this example:
>
>=C2=A0 =C2=A0 (let ((... 'hello))
>=C2=A0 =C2=A0 =C2=A0 (let-syntax ((foo (syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 ((foo x ...)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0'((x) ...)))))
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 (foo 1 2)))
>
>=C2=A0 If '...' is recognized as an ellipsis within the 'le= t', then the result
>=C2=A0 will be '((1) (2)).=C2=A0 Otherwise, the result will be '= ;((1) 2).
>
> I just tested with Chez Scheme 9.5 as well. It returns the same result= as Chibi, namely '((1) 2).
>=C2=A0
>=C2=A0 I found that Racket 7.0, Chicken 4.13.0, and Scheme48 1.9.2 retu= rn
>=C2=A0 '((1) (2)).=C2=A0 Chibi-Scheme returns '((1) 2).=C2=A0 I= see the same results
>=C2=A0 with this variant:
>
>=C2=A0 =C2=A0 (let-syntax ((... (syntax-rules ())))
>=C2=A0 =C2=A0 =C2=A0 (let-syntax ((foo (syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 ((foo x ...)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0'((x) ...)))))
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 (foo 1 2)))
>
> Again, Chez returns '((1) 2).
>
>=C2=A0 If we instead bind '...' as a top-level variable:
>
>=C2=A0 =C2=A0 (define-syntax ... (syntax-rules ()))
>=C2=A0 =C2=A0 (let-syntax ((foo (syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 ((foo x ...)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0'((x) ...)))))
>=C2=A0 =C2=A0 =C2=A0 (foo 1 2))
>
>=C2=A0 Then all four of these Schemes agree that the answer is '((1= ) (2)),
>=C2=A0 including Chibi-Scheme.
>
> Chez Scheme still returns '((1) 2) and thus does not recognize `..= .'
> as the ellipsis.

I stand by the analysis in my previous response in this thread, but
nonetheless I've since realized that given the current implementation o= f
'ellipsis?' in psyntax.scm, Guile *should* be returning '((1) 2= ) in
these examples above, and I'm not sure why that's not happening.
The reason is that when no ellipsis binding is present, Guile currently
uses 'free-identifier=3D?' to compare identifiers with #'(... .= ..) in
psyntax.scm, where '...' has no binding.=C2=A0 In the examples abov= e, the
ellipsis identifiers are within a lexical environment where '...' i= s
bound, so 'free-identifier=3D?' should be returning #false in these= cases.

Having said this, given the analysis in my previous response, I'm now wondering whether it's a mistake to use 'free-identifier=3D?' t= o check for
the default ellipsis '...'.=C2=A0

= Using `free-identifier=3D?' to check for the default ellipsis is defini= tely correct. `bound-identifier=3D?' would not give the correct results= in R[67]RS because the ellipsis can be imported under several names. Or us= e a macro that produces aliases of the ellipsis:

(= import (scheme base))

(define-syntax foo
=C2=A0 (syntax-rules ()
=C2=A0 =C2=A0 ((foo e) TEMPLATE)))
=

(foo ...)

In TEMPLATE, both `.= ..' and `e' have to be the ellipsis. They are `free-identifier=3D?&= #39; but not `bound-identifier=3D?'.
=C2=A0
Within the lexical scope of
'with-ellipsis', Guile uses 'bound-identifier=3D?' to check= for the
user-defined ellipsis, but perhaps we should be using it uniformly to
check for ellipsis in all cases.

Using = `free-identifier=3D?' would only make sense in a model, in which `(with= -ellipsis E BODY)' actually binds `E', wouldn't it? In any case= , inside the scope of `with-ellipsis', the behavior of `syntax-case'= ;, `syntax' differs substantially with respect to the ellipsis from the= behavior outside of `with-ellipsis'. Thus it coud very well be that we= need `bound-identifier=3D?' in scopes inside `with-ellipsis' and `= free-identifier=3D?' in scopes outside.=C2=A0

P.S.:

Thought about it a = bit more. I think it would work if we used `free-identifier=3D?' everyw= here, which means: `(with-ellipsis E BODY)' stores the current binding = of `E' in CURRENT-ELLIPSIS-BINDING. In body, `(ellipsis-identifier? x)&= #39; then compares the binding of `x' with CURRENT-ELLIPSIS-BINDING.

This should be compatible with SRFI-46/R7RS: `(synta= x-rules ::: (<id> ---) <rule> ---)' would be rewritten as `= (let-syntax ((::: (auxiliary-syntax))) (with-ellpsis ::: (syntax-rules (<= ;id> ...) <rule> ---)'.

Does this mak= e sense?=C2=A0

-- Marc

= =C2=A0
What do you think?

Also, for the sake of completeness I should explain a remaining detail
of Guile's semantics for 'with-ellipsis' bindings.=C2=A0 In my = previous
response, I wrote:

>=C2=A0 I think it makes more sense to model (with-ellipsis E BODY) as >=C2=A0 introducing a new lexical binding with a fixed name.=C2=A0 CURRE= NT-ELLIPSIS
>=C2=A0 would be a good name if we wanted to make it part of the public = API.=C2=A0 In
>=C2=A0 this model, 'E' becomes the _value_ of the binding, inst= ead of the
>=C2=A0 binding's name.

In existing versions of Guile, the special fixed name is equivalent to:

=C2=A0 (datum->syntax E '#{ $sc-ellipsis }#)

Where E is the identifier being bound or checked.=C2=A0 In particular, the<= br> _marks_ from E are copied to the special fixed name, both when binding a new ellipsis identifier and when checking for ellipsis identifier.=C2=A0 In=
other words, for each set of marks there's a distinct ellipsis binding.=

That is actually good for hygiene as t= he following two examples (from an earlier post in this thread) show:
=

So this one indeed outputs #= f, thus reproducing your result.

(eval-when (expand)
=C2=A0 (define-syntax bar2
=C2=A0 =C2=A0 (syntax-rules ()
=
=C2=A0 =C2=A0 =C2=A0 ((_ e body)
<= /div>
=C2=A0 =C2=A0 =C2=A0 =C2=A0(with-ellipsis e body)))))=
<= font face=3D"monospace, monospace">
(define-syntax foo2
=C2=A0= (lambda (stx)
=C2=A0 =C2=A0 (bar2 = e (syntax-case stx ()
=C2=A0 =C2=A0 =C2=A0 ((_a ...)
<= /div>
=C2=A0 =C2=A0 = =C2=A0 =C2=A0#'#t)
=C2=A0 =C2=A0 =C2=A0 ((_ a b c)
=C2=A0 =C2=A0 =C2=A0 =C2=A0#'#f)))))
=

(display (foo2 1 2 = 3))
(newline)
<= /div>

On the other hand, this one prints #t.

(eval-when (expand)
=C2=A0 (define-syntax bar2
=C2=A0 =C2=A0 (syntax-rules ()
=
= =C2=A0 =C2=A0 =C2=A0 ((_ e body)
=C2=A0 =C2= =A0 =C2=A0 =C2=A0(with-ellipsis f body))))) ; THE DIFFERENCE IS HERE.

(define-syntax foo2
=C2=A0 (lambda (stx)
=C2=A0 =C2=A0 (bar2 e= (syntax-case stx ()
=C2=A0 =C2=A0 =C2=A0 ((_a ...)
=C2=A0 =C2=A0 =C2=A0 =C2=A0#'#t)
=C2=A0 =C2=A0 =C2=A0 ((_ a b c)
=C2=A0 =C2=A0 =C2=A0 =C2=A0#'#f)))))=

(display = (foo2 1 2 3))
(newline)
=C2=A0
WARNING: Note that the name #{ $s= c-ellipsis }# is not part of Guile's
API, and is subject to change.=C2=A0 We reserve the right to change its nam= e,
or to make it completely inaccessible in future versions of Guile.=C2=A0

I think this would be a good move. Internal = names shouldn't be forgeable by user code. One may think that no one wo= uld name their identifiers `{# $sc-ellipsis }#', but these kind of issu= es can and do happen in meta-compiling, code reflection, etc.
=C2= =A0
For example, we = might move the ellipsis bindings out of the substitution and
into a separate field in the wrap which maps marks to ellipsis
identifiers, with no name involved at all.

<= div>Such an approach could be generalized to an API that allows to attach c= ustom properties to a lexical scope.
=C2=A0
I welcome input on these questions.

Anytime, although I should mention that I am= not an expert.

-- Marc

=


--
Prof. Dr. Mar= c Nieper-Wi=C3=9Fkirchen
=C2=A0
Universit=C3=A4t Augsburg
Institut= f=C3=BCr Mathematik
Universit=C3=A4tsstra=C3=9Fe 14
86159 Augsburg=C2=A0
Tel: 0821/598-2146
Fax: 0821/598-2090
=C2=A0
E-Mail: <= a href=3D"mailto:marc.nieper-wisskirchen@math.uni-augsburg.de" target=3D"_b= lank">marc.nieper-wisskirchen@math.uni-augsburg.de
Web: = www.math.uni-augsburg.de/alg/mitarbeiter/mnieper/
--000000000000b1b244057b6573e2--