Re: Gelex with Unicode

"Eric Bezault ericb-D6Qt/9opevxWk0Htik3J/[email protected] [gobo-eiffel]" <[email protected]> Tue, 30 Jun 2020 00:30:31 +0200
Newsgroups gmane.comp.lang.eiffel.gobo.general
Message-ID <[email protected]>
--Z9r76uBT5XZVJtoqbbqCAsod0eQuOHhwRW1WQE-
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

Hello Alexander,

I'm sorry to hear that you have troubles using gelex. It has been
some times now since I worked on it, so I don't remember all the
details. But what I remember is that I worked hard to make gelex
work in a flexible way so that it could still work with existing
..l files as well as with .l files containing Unicode characters.
And as far as I know, we can combine various input buffers. But
it's like any Eiffel program where you pass a STRING_8 to a
routine for example. If the routine expects this string to actually
contain UTF-8 bytes and you pass "é" instead of the sequence
of two bytes corresponding to this character in UTF-8 encoding,
then the routine will not work as expected. But you don't get
a compilation error. Same here for gelex.

But in fact things are simpler than they might look like after
reading your message below. So let me present it in a simple
way:

* The scanner generated by gelex can accept CHARACTER_8 or
   CHARACTER_32 sequences, regardless of whether the patterns
   in the scanner are written with ISO 8859-1 characters or
   Unicode characters.

* The input buffer reads files and produce sequences of
   characters (either CHARACTER_8 or CHARACTER_32) that
   will be sent to the scanner.

* YY_FILE_BUFFER reads bytes as they are in the input file
   and produces a sequence of CHARACTER_8, one CHARACTER_8
   per byte (including the bytes of the BOM if present).
   This means that 'é' will be represented as a single
   CHARACTER_8 character.

* YY_UTF8_FILE_BUFFER can read files containing ISO 8859-1
   characters (no BOM) or Unicode characters (with UTF-8 BOM).
   It will produce a sequence of CHARACTER_8 corresponding
   to the bytes of the UTF-8 representation of what has been
   read. So if the input file was encoded in UTF-8, it will
   skip the BOM and then send the bytes unchanged. If it
   was ISO 8859-1, it will convert the characters to UTF-8
   (e.g. the character 'é' will be replaced by its two byte
   UTF-8 encoding).

* YY_UNICODE_FILE_BUFFER can read files containing ISO 8859-1
   characters (no BOM) or Unicode characters (with UTF-8 BOM).
   It will produce a sequence of CHARACTER_32 characters.
   So if the input file was encoded in UTF-8, it will replace
   the UTF-8 byte sequences with their corresponding Unicode
   characters. If it was ISO 8859-1, it will just convert the
   characters to CHARACTER_32 (e.g. the character 'é' will
   be converted to the character {CHARACTER_32}'é').

Now, you can write a scanner with patterns written in
ISO 8859-1 and use a YY_UNICODE_FILE_BUFFER. If will
work. Internally the scanner uses character codes
as indexes to internal tables. And since {CHARACTER_8}'é'
and {CHARACTER_32}'é' have the same code, it will
match. Of course if the input file contains Unicode
characters with code greater than 255, they will not
match the patterns in the .l file which only contains
characters with codes less than 255. And similarly,
you can write a scanner with patterns written in
Unicode and use YY_FILE_BUFFER. If the pattern is
of the form '[a∀]' and the input is 'a' then it will
match. See the section "Some notes on patterns" in
http://www.gobosoft.com/eiffel/gobo/gelex/patterns.html
about the use of Unicode characters in patterns.
And of course, if you get a Unicode string which
cannot be represented as  a STRING_8 and you call
`text` (instead of `unicode_text` or `utf8_text`) you get
some truncated results: 
http://www.gobosoft.com/eiffel/gobo/gelex/actions.html#text

So far, we had scanners:

* which were expecting ISO 8859-1 input files and were using
   YY_FILE_BUFFER
* which were expecting Unicode input files encoded in UTF-8
   and which were using an equivalent of YY_UTF8_FILE_BUFFER
   and using patterns containing the explicit UTF-8 byte
   sequences (which is the case of the Eiffel scanner in
   EiffelStudio: 
https://github.com/EiffelSoftware/EiffelStudio/blob/master/Src/framework/parser/parser/eiffel.l 
)

and now we have scanners:

* which are expecting Unicode input files encoded in UTF-8
   and which are using YY_UNICODE_FILE_BUFFER and using
   patterns containing Unicode characters (which is the case
   of the Eiffel scanner in Gobo: 
https://github.com/gobo-eiffel/gobo/blob/master/library/tools/src/eiffel/parser/et_eiffel_scanner.l 
)


But of course, if you write patterns containing Unicode characters
and you use a file buffer which sends UTF-8 bytes that will not
work because the bytes will be received as CHARACTER_8 and
interpreted as characters. It's like the example that I gave at the
beginning of this message where you have a routine with an
argument of type STRING_8 and it expects a UTF-8 sequence of bytes
and you pass "é" (or vice-versa).

And to complicate things, we also have the option utf8:
http://www.gobosoft.com/eiffel/gobo/gelex/options.html#utf8
Initially, the idea to support Unicode in gelex was to allow
patterns to be written with Unicode characters, but let gelex
convert them to UTF-8 byte sequences behind the scene, and
hence generate the same kind of scanner that is currently
used by EiffelStudio. This scanner expects a sequence of
UTF-8 bytes as input, e.g. YY_UTF8_FILE_BUFFER, or
YY_FILE_BUFFER if the input file only contains ASCII
characters. But I don't recommend using this mode because
in most cases it is not as good as using YY_UNICODE_FILE_BUFFER.
But I mention it here because this option is what you were
missing when you tried to use patterns containing Unicode
characters with a YY_UTF8_FILE_BUFFER.

One last remark: the input buffers are not limited to
YY_FILE_BUFFER, YY_UTF8_FILE_BUFFER and YY_UNICODE_FILE_BUFFER.
One can write another class which would read files encoded
with UTF-16 and produce a sequence of CHARACTER_32 (or even
a sequence of UTF-8 bytes).

And as a reminder, you can have a look at this small example:
https://github.com/gobo-eiffel/gobo/tree/master/library/lexical/example/unicode

-- 
Eric Bezault
mailto:ericb-D6Qt/9opevxWk0Htik3J/[email protected]
http://www.gobosoft.com


On 29/06/2020 19:05, Alexander Kogtenkov kwaxer-JGs/[email protected] [gobo-eiffel] wrote:
> 
> 
> Dear Eric,
> The recent addition of Unicode support in gelex looks great. However, 
> when using it, the current API makes an illusion that all scanner 
> buffers are replaceable for any variants of generated scanners. This 
> does not seem to be the case.
> Ideally, I’d rather get a compilation error when I’m using a wrong 
> combination of input buffers and generated scanners. I do not know 
> whether this is easily achievable.
> If not, it would be great to see what combinations are valid/working and 
> under which circumstances.
> So far by trial and error I discovered that the following combinations work:
> 
>  1. ISO 8859-1 scanner (no BOM in .l file) using patterns with explicit
>     UTF-8 byte sequences, {YY_SCANNER_SKELETON}.(text,
>     append_text_to_string, append_text_substring_to_string,
>     yy_content_area), YY_FILE_BUFFER.
>  2. Unicode scanner (BOM in .l file) using patterns with Unicode code
>     points, {YY_SCANNER_SKELETON}.(utf8_text,
>     append_utf8_text_to_string, append_utf8_text_substring_to_string,
>     yy_unicode_area), YY_UNICODE_FILE_BUFFER with default encoding set
>     to UTF-8.
> 
> Both combinations properly support UTF-8-encoded files.
> I thought, YY_UTF8_FILE_BUFFER could replace YY_UNICODE_FILE_BUFFER in 
> item 2. My naïve understanding was that it would deliver Unicode 
> characters reading them from a UTF-8 encoded file on the fly instead of 
> pre-converting them when loading from the file. But it simply delivers 
> UTF-8 bytes, not code points, and the scanner does not work as I would 
> expect. What is the use case for it then? A bit of automation for item 1 
> when source file is not in UTF-8?
> More generally, I can see the following variations:
> a. Scanner description file .l has BOM or not.
> b. A client of YY_SCANNER_SKELETON uses `text`, `utf8_text`, 
> `unicode_text` (and related).
> c. There are YY_FILE_BUFFER, YY_UNICODE_FILE_BUFFER, YY_UTF8_FILE_BUFFER.
> d. The scanner can be generated with and without option `utf8`.
> This gives 12 (2*3*3*2) combinations. Which of them are usable and under 
> what conditions (e.g., what are expectation for input and for output)? 
> Is there something like a table describing the possibilities?
> Anyway, would on-the-fly conversion from UTF-8 to Unicode to use Unicode 
> scanner with UTF-8-encoded files be a bad idea? Probably, the idea is 
> not good, because it would not allow for code inlining. But there are 
> other factors in play, like memory locality, memory footprint etc.
> Thank you,
> Alexander Kogtenkov
> 
> 
> 




--Z9r76uBT5XZVJtoqbbqCAsod0eQuOHhwRW1WQE-
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable




<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/htm=
l4/strict.dtd">
<html>
<head>
</head>






=20
<body style=3D"background-color: #fff;">
<span style=3D"display:none">&nbsp;</span>

<!--~-|**|PrettyHtmlStartT|**|-~-->
<div id=3D"ygrp-mlmsg" style=3D"position:relative;">
  <div id=3D"ygrp-msg" style=3D"z-index: 1;">
<!--~-|**|PrettyHtmlEndT|**|-~-->

    <div id=3D"ygrp-text" >
=20=20=20=20=20=20
=20=20=20=20=20=20
      <p>Hello Alexander,<br>
<br>
I&#39;m sorry to hear that you have troubles using gelex. It has been<br>
some times now since I worked on it, so I don&#39;t remember all the<br>
details. But what I remember is that I worked hard to make gelex<br>
work in a flexible way so that it could still work with existing<br>
..l files as well as with .l files containing Unicode characters.<br>
And as far as I know, we can combine various input buffers. But<br>
it&#39;s like any Eiffel program where you pass a STRING_8 to a<br>
routine for example. If the routine expects this string to actually<br>
contain UTF-8 bytes and you pass &quot;=C3=A9&quot; instead of the sequence=
<br>
of two bytes corresponding to this character in UTF-8 encoding,<br>
then the routine will not work as expected. But you don&#39;t get<br>
a compilation error. Same here for gelex.<br>
<br>
But in fact things are simpler than they might look like after<br>
reading your message below. So let me present it in a simple<br>
way:<br>
<br>
* The scanner generated by gelex can accept CHARACTER_8 or<br>
   CHARACTER_32 sequences, regardless of whether the patterns<br>
   in the scanner are written with ISO 8859-1 characters or<br>
   Unicode characters.<br>
<br>
* The input buffer reads files and produce sequences of<br>
   characters (either CHARACTER_8 or CHARACTER_32) that<br>
   will be sent to the scanner.<br>
<br>
* YY_FILE_BUFFER reads bytes as they are in the input file<br>
   and produces a sequence of CHARACTER_8, one CHARACTER_8<br>
   per byte (including the bytes of the BOM if present).<br>
   This means that &#39;=C3=A9&#39; will be represented as a single<br>
   CHARACTER_8 character.<br>
<br>
* YY_UTF8_FILE_BUFFER can read files containing ISO 8859-1<br>
   characters (no BOM) or Unicode characters (with UTF-8 BOM).<br>
   It will produce a sequence of CHARACTER_8 corresponding<br>
   to the bytes of the UTF-8 representation of what has been<br>
   read. So if the input file was encoded in UTF-8, it will<br>
   skip the BOM and then send the bytes unchanged. If it<br>
   was ISO 8859-1, it will convert the characters to UTF-8<br>
   (e.g. the character &#39;=C3=A9&#39; will be replaced by its two byte<br=
>
   UTF-8 encoding).<br>
<br>
* YY_UNICODE_FILE_BUFFER can read files containing ISO 8859-1<br>
   characters (no BOM) or Unicode characters (with UTF-8 BOM).<br>
   It will produce a sequence of CHARACTER_32 characters.<br>
   So if the input file was encoded in UTF-8, it will replace<br>
   the UTF-8 byte sequences with their corresponding Unicode<br>
   characters. If it was ISO 8859-1, it will just convert the<br>
   characters to CHARACTER_32 (e.g. the character &#39;=C3=A9&#39; will<br>
   be converted to the character {CHARACTER_32}&#39;=C3=A9&#39;).<br>
<br>
Now, you can write a scanner with patterns written in<br>
ISO 8859-1 and use a YY_UNICODE_FILE_BUFFER. If will<br>
work. Internally the scanner uses character codes<br>
as indexes to internal tables. And since {CHARACTER_8}&#39;=C3=A9&#39;<br>
and {CHARACTER_32}&#39;=C3=A9&#39; have the same code, it will<br>
match. Of course if the input file contains Unicode<br>
characters with code greater than 255, they will not<br>
match the patterns in the .l file which only contains<br>
characters with codes less than 255. And similarly,<br>
you can write a scanner with patterns written in<br>
Unicode and use YY_FILE_BUFFER. If the pattern is<br>
of the form &#39;[a=E2=88=80]&#39; and the input is &#39;a&#39; then it wil=
l<br>
match. See the section &quot;Some notes on patterns&quot; in<br>
http://www.gobosoft.com/eiffel/gobo/gelex/patterns.html<br>
about the use of Unicode characters in patterns.<br>
And of course, if you get a Unicode string which<br>
cannot be represented as  a STRING_8 and you call<br>
`text` (instead of `unicode_text` or `utf8_text`) you get<br>
some truncated results: <br>
http://www.gobosoft.com/eiffel/gobo/gelex/actions.html#text<br>
<br>
So far, we had scanners:<br>
<br>
* which were expecting ISO 8859-1 input files and were using<br>
   YY_FILE_BUFFER<br>
* which were expecting Unicode input files encoded in UTF-8<br>
   and which were using an equivalent of YY_UTF8_FILE_BUFFER<br>
   and using patterns containing the explicit UTF-8 byte<br>
   sequences (which is the case of the Eiffel scanner in<br>
   EiffelStudio: <br>
https://github.com/EiffelSoftware/EiffelStudio/blob/master/Src/framework/pa=
rser/parser/eiffel.l <br>
)<br>
<br>
and now we have scanners:<br>
<br>
* which are expecting Unicode input files encoded in UTF-8<br>
   and which are using YY_UNICODE_FILE_BUFFER and using<br>
   patterns containing Unicode characters (which is the case<br>
   of the Eiffel scanner in Gobo: <br>
https://github.com/gobo-eiffel/gobo/blob/master/library/tools/src/eiffel/pa=
rser/et_eiffel_scanner.l <br>
)<br>
<br>
But of course, if you write patterns containing Unicode characters<br>
and you use a file buffer which sends UTF-8 bytes that will not<br>
work because the bytes will be received as CHARACTER_8 and<br>
interpreted as characters. It&#39;s like the example that I gave at the<br>
beginning of this message where you have a routine with an<br>
argument of type STRING_8 and it expects a UTF-8 sequence of bytes<br>
and you pass &quot;=C3=A9&quot; (or vice-versa).<br>
<br>
And to complicate things, we also have the option utf8:<br>
http://www.gobosoft.com/eiffel/gobo/gelex/options.html#utf8<br>
Initially, the idea to support Unicode in gelex was to allow<br>
patterns to be written with Unicode characters, but let gelex<br>
convert them to UTF-8 byte sequences behind the scene, and<br>
hence generate the same kind of scanner that is currently<br>
used by EiffelStudio. This scanner expects a sequence of<br>
UTF-8 bytes as input, e.g. YY_UTF8_FILE_BUFFER, or<br>
YY_FILE_BUFFER if the input file only contains ASCII<br>
characters. But I don&#39;t recommend using this mode because<br>
in most cases it is not as good as using YY_UNICODE_FILE_BUFFER.<br>
But I mention it here because this option is what you were<br>
missing when you tried to use patterns containing Unicode<br>
characters with a YY_UTF8_FILE_BUFFER.<br>
<br>
One last remark: the input buffers are not limited to<br>
YY_FILE_BUFFER, YY_UTF8_FILE_BUFFER and YY_UNICODE_FILE_BUFFER.<br>
One can write another class which would read files encoded<br>
with UTF-16 and produce a sequence of CHARACTER_32 (or even<br>
a sequence of UTF-8 bytes).<br>
<br>
And as a reminder, you can have a look at this small example:<br>
https://github.com/gobo-eiffel/gobo/tree/master/library/lexical/example/uni=
code<br>
<br>
-- <br>
Eric Bezault<br>
mailto:ericb-D6Qt/9opevxWk0Htik3J/[email protected]<br>
http://www.gobosoft.com<br>
<br>
On 29/06/2020 19:05, Alexander Kogtenkov kwaxer-JGs/[email protected] [gobo-eiffel] wrote=
:<br>
&gt; <br>
&gt; <br>
&gt; Dear Eric,<br>
&gt; The recent addition of Unicode support in gelex looks great. However, =
<br>
&gt; when using it, the current API makes an illusion that all scanner <br>
&gt; buffers are replaceable for any variants of generated scanners. This <=
br>
&gt; does not seem to be the case.<br>
&gt; Ideally, I=E2=80=99d rather get a compilation error when I=E2=80=99m u=
sing a wrong <br>
&gt; combination of input buffers and generated scanners. I do not know <br=
>
&gt; whether this is easily achievable.<br>
&gt; If not, it would be great to see what combinations are valid/working a=
nd <br>
&gt; under which circumstances.<br>
&gt; So far by trial and error I discovered that the following combinations=
 work:<br>
&gt; <br>
&gt;  1. ISO 8859-1 scanner (no BOM in .l file) using patterns with explici=
t<br>
&gt;     UTF-8 byte sequences, {YY_SCANNER_SKELETON}.(text,<br>
&gt;     append_text_to_string, append_text_substring_to_string,<br>
&gt;     yy_content_area), YY_FILE_BUFFER.<br>
&gt;  2. Unicode scanner (BOM in .l file) using patterns with Unicode code<=
br>
&gt;     points, {YY_SCANNER_SKELETON}.(utf8_text,<br>
&gt;     append_utf8_text_to_string, append_utf8_text_substring_to_string,<=
br>
&gt;     yy_unicode_area), YY_UNICODE_FILE_BUFFER with default encoding set=
<br>
&gt;     to UTF-8.<br>
&gt; <br>
&gt; Both combinations properly support UTF-8-encoded files.<br>
&gt; I thought, YY_UTF8_FILE_BUFFER could replace YY_UNICODE_FILE_BUFFER in=
 <br>
&gt; item 2. My na=C3=AFve understanding was that it would deliver Unicode =
<br>
&gt; characters reading them from a UTF-8 encoded file on the fly instead o=
f <br>
&gt; pre-converting them when loading from the file. But it simply delivers=
 <br>
&gt; UTF-8 bytes, not code points, and the scanner=C2=A0does not work as I =
would <br>
&gt; expect. What is the use case for it then? A bit of automation for item=
 1 <br>
&gt; when source file is not in UTF-8?<br>
&gt; More generally, I can see the following variations:<br>
&gt; a. Scanner description file .l has BOM or not.<br>
&gt; b. A client of YY_SCANNER_SKELETON uses `text`, `utf8_text`, <br>
&gt; `unicode_text` (and related).<br>
&gt; c. There are YY_FILE_BUFFER, YY_UNICODE_FILE_BUFFER, YY_UTF8_FILE_BUFF=
ER.<br>
&gt; d. The scanner can be generated with and without option `utf8`.<br>
&gt; This gives 12 (2*3*3*2) combinations. Which of them are usable and und=
er <br>
&gt; what conditions (e.g., what are expectation for input and for output)?=
 <br>
&gt; Is there something like a table describing the possibilities?<br>
&gt; Anyway, would on-the-fly conversion from UTF-8 to Unicode to use Unico=
de <br>
&gt; scanner with UTF-8-encoded files be a bad idea? Probably, the idea is =
<br>
&gt; not good, because it would not allow for code inlining. But there are =
<br>
&gt; other factors in play, like memory locality, memory footprint etc.<br>
&gt; Thank you,<br>
&gt; Alexander Kogtenkov<br>
&gt; <br>
&gt; <br>
&gt; <br>
<br>
</p>

    </div>
=20=20=20=20=20

    <!--~-|**|PrettyHtmlStart|**|-~-->
    <div style=3D"color: #fff; height: 0;">__._,_.___</div>

=20=20=20=20=20=20=20=20=20=20
=20=20
=20

=20=20=20=20
    <div style=3D"clear:both"> </div>

    <div id=3D"fromDMARC" style=3D"margin-top: 10px;">
        <hr style=3D"height:2px ; border-width:0; color:#E3E3E3; background=
-color:#E3E3E3;">
        Posted by: Eric Bezault &lt;ericb-D6Qt/9opevxWk0Htik3J/[email protected]&gt;        <hr style=
=3D"height:2px ; border-width:0; color:#E3E3E3; background-color:#E3E3E3;">
     </div>
    <div style=3D"clear:both"> </div>

    <table cellspacing=3D4px style=3D"margin-top: 10px; margin-bottom: 10px=
; color: #2D50FD;">
      <tbody>
        <tr>
          <td style=3D"font-size: 12px; font-family: arial; font-weight: bo=
ld; padding: 7px 5px 5px;"  >
                          <a style=3D"text-decoration: none; color: #2D50FD=
" href=3D"https://groups.yahoo.com/neo/groups/gobo-eiffel/conversations/mes=
sages/1924;_ylc=3DX3oDMTJwY29sanA5BF9TAzk3MzU5NzE0BGdycElkAzE1MjIyNzAEZ3Jwc=
3BJZAMxNzA1MDA2NzY0BG1zZ0lkAzE5MjQEc2VjA2Z0cgRzbGsDcnBseQRzdGltZQMxNTkzNDY5=
ODM3?act=3Dreply&messageNum=3D1924">Reply via web post</a>
                      </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;" >
            <a href=3D"mailto:ericb-D6Qt/9opevxWk0Htik3J/[email protected]?subject=3DRe%3A%20%5Bgobo-=
eiffel%5D%20Gelex%20with%20Unicode" style=3D"text-decoration: none; color: =
#2D50FD;">
               Reply to sender            </a>
          </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;">
            <a href=3D"mailto:[email protected]?subject=3DRe%3A%2=
0%5Bgobo-eiffel%5D%20Gelex%20with%20Unicode" style=3D"text-decoration: none=
; color: #2D50FD">
              Reply to group            </a>
          </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;" >
            <a href=3D"https://groups.yahoo.com/neo/groups/gobo-eiffel/conv=
ersations/newtopic;_ylc=3DX3oDMTJlZWFucjZpBF9TAzk3MzU5NzE0BGdycElkAzE1MjIyN=
zAEZ3Jwc3BJZAMxNzA1MDA2NzY0BHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTU5MzQ2OTgzNw--=
" style=3D"text-decoration: none; color: #2D50FD">Start a New Topic</a>
          </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;color: #2D50FD;" >
                            <a href=3D"https://groups.yahoo.com/neo/groups/=
gobo-eiffel/conversations/topics/1923;_ylc=3DX3oDMTM0aWFldm1tBF9TAzk3MzU5Nz=
E0BGdycElkAzE1MjIyNzAEZ3Jwc3BJZAMxNzA1MDA2NzY0BG1zZ0lkAzE5MjQEc2VjA2Z0cgRzb=
GsDdnRwYwRzdGltZQMxNTkzNDY5ODM3BHRwY0lkAzE5MjM-" style=3D"text-decoration: =
none; color: #2D50FD;">Messages in this topic</a>
                (2)
                      </td>
        </tr>
      </tbody>
    </table>

=20=20=20=20=20=20=20=20

<!------- Start Nav Bar ------>
<!-- |**|begin egp html banner|**| -->
<!-- |**|end egp html banner|**| -->


    <div id=3D"ygrp-grfd" style=3D"font-family: Verdana; font-size: 12px; p=
adding: 15px 0;">
=20=20=20=20=20=20
<!-- |**|begin egp html banner|**| -->

      To Post a message, send it to:&nbsp;&nbsp; [email protected]<BR=
>
To Unsubscribe, send a blank message to: gobo-eiffel-unsubscribe-B11MqFFcr06IwRZHo2/[email protected]=
m=20=20=20=20=20=20
<!-- |**|end egp html banner|**| -->

    </div>
=20=20

=20

<!-- |**|begin egp html banner|**| -->
<div id=3D"ygrp-vital" style=3D"background-color: #f2f2f2; font-family: Ver=
dana; font-size: 10px; margin-bottom: 10px; padding: 10px;">

    <span id=3D"vithd" style=3D"font-weight: bold; color: #333; text-transf=
orm: uppercase; "><a href=3D"https://groups.yahoo.com/neo/groups/gobo-eiffe=
l/info;_ylc=3DX3oDMTJlOWttcmRhBF9TAzk3MzU5NzE0BGdycElkAzE1MjIyNzAEZ3Jwc3BJZ=
AMxNzA1MDA2NzY0BHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTU5MzQ2OTgzNw--" style=3D"t=
ext-decoration: none;">Visit Your Group</a></span>

     <ul style=3D"list-style-type: none; margin: 0; padding: 0; display: in=
line;">
                                                    </ul>
  </div>


<div id=3D"ft" style=3D"font-family: Arial; font-size: 11px; margin-top: 5p=
x; padding: 0 2px 0 0; clear: both;">
  <a href=3D"https://groups.yahoo.com/neo;_ylc=3DX3oDMTJkN2pnZGdtBF9TAzk3Mz=
U5NzE0BGdycElkAzE1MjIyNzAEZ3Jwc3BJZAMxNzA1MDA2NzY0BHNlYwNmdHIEc2xrA2dmcARzd=
GltZQMxNTkzNDY5ODM3" style=3D"float: left;"><img src=3D"https://s.yimg.com/=
ru/0.9.12/min/css/yahoo_en-US_f_pw_101x21.png" height=3D"21" width=3D"101" =
alt=3D"Yahoo! Groups" style=3D"border: 0;"/></a>
  <div style=3D"color: #747575; float: right;"> &bull; <a href=3D"https://i=
nfo.yahoo.com/privacy/us/yahoo/groups/details.html" style=3D"text-decoratio=
n: none;">Privacy</a> &bull; <a href=3D"mailto:gobo-eiffel-unsubscribe@yaho=
ogroups.com?subject=3DUnsubscribe" style=3D"text-decoration: none;">Unsubsc=
ribe</a> &bull; <a href=3D"https://info.yahoo.com/legal/us/yahoo/utos/terms=
/" style=3D"text-decoration: none;">Terms of Use</a> </div>
</div>
<br>

<!-- |**|end egp html banner|**| -->

  </div> <!-- ygrp-msg -->

=20
  <!-- Sponsor -->
  <!-- |**|begin egp html banner|**| -->
  <div id=3D"ygrp-sponsor" style=3D"width:160px; float:right; clear:none; m=
argin:0 0 25px 0; background: #fff;">

<!-- Start Recommendations -->
<div id=3D"ygrp-reco">
     </div>
<!-- End Recommendations -->



  </div>   <!-- |**|end egp html banner|**| -->

  <div style=3D"clear:both; color: #FFF; font-size:1px;">.</div>
</div>

  <img src=3D"http://geo.yahoo.com/serv?s=3D97359714/grpId=3D1522270/grpspI=
d=3D1705006764/msgId=3D1924/stime=3D1593469837" width=3D"1" height=3D"1"> <=
br>

<img src=3D"http://y.analytics.yahoo.com/fpc.pl?ywarid=3D515FB27823A7407E&a=
=3D10001310322279&js=3Dno&resp=3Dimg" width=3D"1" height=3D"1">=20

<div style=3D"color: #fff; height: 0;">__,_._,___</div>
<!--~-|**|PrettyHtmlEnd|**|-~-->

</body>

<!--~-|**|PrettyHtmlStart|**|-~-->
<head>
  <style type=3D"text/css">
  <!--
  #ygrp-mkp {
  border: 1px solid #d8d8d8;
  font-family: Arial;
  margin: 10px 0;
  padding: 0 10px;
}

#ygrp-mkp hr {
  border: 1px solid #d8d8d8;
}

#ygrp-mkp #hd {
  color: #628c2a;
  font-size: 85%;
  font-weight: 700;
  line-height: 122%;
  margin: 10px 0;
}

#ygrp-mkp #ads {
  margin-bottom: 10px;
}

#ygrp-mkp .ad {
  padding: 0 0;
}

#ygrp-mkp .ad p {
  margin: 0;
}

#ygrp-mkp .ad a {
  color: #0000ff;
  text-decoration: none;
}
  #ygrp-sponsor #ygrp-lc {
  font-family: Arial;
}

#ygrp-sponsor #ygrp-lc #hd {
  margin: 10px 0px;
  font-weight: 700;
  font-size: 78%;
  line-height: 122%;
}

#ygrp-sponsor #ygrp-lc .ad {
  margin-bottom: 10px;
  padding: 0 0;
}

  #actions {
    font-family: Verdana;
    font-size: 11px;
    padding: 10px 0;
  }

  #activity {
    background-color: #e0ecee;
    float: left;
    font-family: Verdana;
    font-size: 10px;
    padding: 10px;
  }

  #activity span {
    font-weight: 700;
  }

  #activity span:first-child {
    text-transform: uppercase;
  }

  #activity span a {
    color: #5085b6;
    text-decoration: none;
  }

  #activity span span {
    color: #ff7900;
  }

  #activity span .underline {
    text-decoration: underline;
  }

  .attach {
    clear: both;
    display: table;
    font-family: Arial;
    font-size: 12px;
    padding: 10px 0;
    width: 400px;
  }

  .attach div a {
    text-decoration: none;
  }

  .attach img {
    border: none;
    padding-right: 5px;
  }

  .attach label {
    display: block;
    margin-bottom: 5px;
  }

  .attach label a {
    text-decoration: none;
  }
=20=20
  blockquote {
    margin: 0 0 0 4px;
  }

  .bold {
    font-family: Arial;
    font-size: 13px;
    font-weight: 700;
  }

  .bold a {
    text-decoration: none;
  }

  dd.last p a {
    font-family: Verdana;
    font-weight: 700;
  }

  dd.last p span {
    margin-right: 10px;
    font-family: Verdana;
    font-weight: 700;
  }

  dd.last p span.yshortcuts {
    margin-right: 0;
  }

  div.attach-table div div a {
    text-decoration: none;
  }

  div.attach-table {
    width: 400px;
  }

  div.file-title a, div.file-title a:active, div.file-title a:hover, div.fi=
le-title a:visited {
    text-decoration: none;
  }

  div.photo-title a, div.photo-title a:active, div.photo-title a:hover, div=
..photo-title a:visited {
    text-decoration: none;
  }

  div#ygrp-mlmsg #ygrp-msg p a span.yshortcuts {
    font-family: Verdana;
    font-size: 10px;
    font-weight: normal;
  }

  .green {
    color: #628c2a;
  }

  .MsoNormal {
    margin: 0 0 0 0;
  }

  o {
    font-size: 0;
  }

  #photos div {
    float: left;
    width: 72px;
  }

  #photos div div {
    border: 1px solid #666666;
    height: 62px;
    overflow: hidden;
    width: 62px;
  }

  #photos div label {
    color: #666666;
    font-size: 10px;
    overflow: hidden;
    text-align: center;
    white-space: nowrap;
    width: 64px;
  }

  #reco-category {
    font-size: 77%;
  }

  #reco-desc {
    font-size: 77%;
  }

  .replbq {
    margin: 4px;
  }

  #ygrp-actbar div a:first-child {
   /* border-right: 0px solid #000;*/
    margin-right: 2px;
    padding-right: 5px;
  }

  #ygrp-mlmsg {
    font-size: 13px;
    font-family: Arial, helvetica,clean, sans-serif;
    *font-size: small;
    *font: x-small;
  }

  #ygrp-mlmsg table {
    font-size: inherit;
    font: 100%;
  }

  #ygrp-mlmsg select, input, textarea {
    font: 99% Arial, Helvetica, clean, sans-serif;
  }

  #ygrp-mlmsg pre, code {
    font:115% monospace;
    *font-size:100%;
  }

  #ygrp-mlmsg * {
    line-height: 1.22em;
  }

  #ygrp-mlmsg #logo {
    padding-bottom: 10px;
  }


  #ygrp-msg p a {
    font-family: Verdana;
  }

  #ygrp-msg p#attach-count span {
    color: #1E66AE;
    font-weight: 700;
  }

  #ygrp-reco #reco-head {
    color: #ff7900;
    font-weight: 700;
  }

  #ygrp-reco {
    margin-bottom: 20px;
    padding: 0px;
  }

  #ygrp-sponsor #ov li a {
    font-size: 130%;
    text-decoration: none;
  }

  #ygrp-sponsor #ov li {
    font-size: 77%;
    list-style-type: square;
    padding: 6px 0;
  }=20

  #ygrp-sponsor #ov ul {
    margin: 0;
    padding: 0 0 0 8px;
  }

  #ygrp-text {
    font-family: Georgia;
  }

  #ygrp-text p {
    margin: 0 0 1em 0;
  }

  #ygrp-text tt {
    font-size: 120%;
  }

  #ygrp-vital ul li:last-child {
    border-right: none !important;=20
  }=20
  -->
  </style>
</head>

<!--~-|**|PrettyHtmlEnd|**|-~-->
</html>
<!-- end group email -->


--Z9r76uBT5XZVJtoqbbqCAsod0eQuOHhwRW1WQE---