implementation of lexical scoping

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

implementation of lexical scoping

Roberto Ierusalimschy-5
This mail explains the general idea we used to implement lexical scoping
in Lua. Sorry for the delay in answering several questions about this.

The main problem for implementing lexical scoping in Lua is that, when we
first see a variable, we do not know whether it will be used by an inner
function, and therefore whether it may have to outlive the function that
created it (as I do not know a better name for them, I will keep calling a
local variable that is used by an inner function an "upvalue"). Instead of
trying to solve this directly (for instance by changing previous code when
a variable is used by inner functions), we took a more dynamic approach:
all local variables live in the stack, as long as its block is active. When
a block that declares an upvalue finishes, then (and only then) the upvalue
is moved from the stack to the heap (that is, to a dynamically allocated
struct that holds its value). At compile time, whenever a block ends, we
know for sure whether it declares upvalues, and then we can generate a
specific opcode, CLOSE, to move those upvalues from the stack to the heap.

Closures now keep pointer to TObjects, instead of TObjects themselves.
Those pointers may point to the stack, if the upvalue is still active, or
to the heap. Those closures are "flat": when we create a closure, all its
upvalues are either on the stack (that is, they are declared in the
immediate enclosing function), or in the closure of the enclosing function
(those may point to lower levels in the stack or to the heap). Any closure
that points to the stack we call an "open" closure, and we keep a list of
all current open closures. Whenever we execute a CLOSE instruction, we
traverse this list correcting any closure that points to those upvalues
which are moving from the stack to the heap. To avoid traversing the whole
list, the list is kept sorted by stack level, so that as soon as we find
a closure that do not need correction we can stop the traversal.
(We also need to traverse this list when we return from a function and
when we handle errors [return from multiple functions].)

What is the cost of all that? Upvalues in their original level are always
in the stack, and are handled like any regular local variable. Upvalues in
inner levels are accessed through opcodes GETUPVAL and SETUPVAL, which
involves only an array indirection:

      case OP_GETUPVAL: {
        int b = GETARG_B(i);
        setobj(ra, cl->upvals[b].val);
        break;
      }

A CLOSE opcode is only issued when there are upvalues to be closed. For
each closure traversed, at least one correction is made. So, the cost is
proportional to the number of upvalues used by a closure and the number of
closures created by the program (this cost can then be credited as a small
increase in the cost of creating closures, a not-so-cheap operation).

The last cost is that, when creating a new closure with open references
(the most common case), we must insert it in its proper place in the list
of open closures. Usually the proper place is right in front of the list,
so the cost is low. But we can build some artificial examples that may make
the cost of creating a new closure proportional to the number of open
closures already created. (For this to be a problem it must involve two
different closures being generated in loops, that is, in bulk quantities).

-- Roberto


Reply | Threaded
Open this post in threaded view
|

Re: implementation of lexical scoping

RLak
Roberto Ierusalimschy escribió:

> This mail explains the general idea we used to implement lexical scoping
> in Lua. Sorry for the delay in answering several questions about this.

No, don't apologise. Thanks for explaining. It's very useful.

(Note: I use the terminology "binding" in the following rather than
"upvalue". The explanation is a little later in the discourse.)

The idea seems very sensible, and is relatively efficient, as far as I can
see.

I remain with one question, and one suggestion. The question is probably
easier: will C closures be presented with the same interface? That is, will
C closures (need to) use some API call to indirect through to the value of
an enclosed binding, and therefore have the ability to rebind, or will they
be presented with "unbound" (resolved) values rather than bindings? There
would seem to be arguments both ways; changing the C interface will render
a lot of code obsolete; on the other hand, allowing C closures to make use
of bindings provides a certain amount of power: in particular, it makes
possible the implementation of user-objects which reference
garbage-collected Lua objects (that is, by implementing user-objects as
collections of bindings, or in other words closures, rather than or in
addition to user-objects as malloc'd RAM).

For example, such an implementation might provide an API like
lua_getbinding(L, i) / lua_setbinding(L, i) which push / pop the stack from
/ to the indexed binding. Moderate compatibility with old code could be
provided with the API lua_pushbindings(L) which pushes (the values of) all
enclosed bindings onto the stack, effectively emulating the current
implementation.

To create a binding in the first place, one would have an API like
lua_newbinding(L) which perhaps popped the stack into a newly created
binding and then pushed the binding (i.e. the reference to the value) back
onto the stack; as I understand it, this is essentially what the CLOSE VM
operation would do. Whether there would be anything which could be done
with this object other than eventually enclose it into one or more closures
is worth thinking about.

It is interesting to observe that, with or without the above C interface,
what is being created is, in effect, fixed-length mutable vectors (and that
the existing implementation is, in effect, fixed-length immutable vectors).
I have speculated (to myself) several times about whether there is an
interesting language feature hidden in this fact.

While a very clever implementation, it is certainly going to be
significantly slower than the current implementation, particularly as it
requires (as far as I can see) a memory allocation for each enclosed
binding ("upvalue"), in addition to the memory allocation for the closure.
In a typical case of a function with a single enclosed binding, this
doubles the memory allocations (and correspondingly the time for garbage
collection.) As you say, this can be seen as an increment to the cost of
creating a closure, which is not cheap, but it is not (or at least should
not be) significantly slower than creating a table. I draw the analogy
because tables are a plausible alternative to lexically-scoped bindings.

I would say that an upvalue is different from a binding by virtue of being
immutable; in that sense, it is truly a value whereas a binding is (a)
variable. I accept that both are useful, but I would venture to say (at the
risk of being shot down by fans of mutability) that upvalues are more
frequent.

Be that as it may, I wonder if it would be possible to achieve the best of
both worlds without complicating the design of the language too much; that
is, whether it would be possible to have both bindings and upvalues. The
question becomes, what is an upvalue in a world of bindings? There are two
possible answers: 1) it is an immutable binding; 2) it is a binding with
only one referrer. In the second case, the syntax of the language could (or
not) enforce immutability. The suggestion "on the table" is the second
alternative; that is, if I want a traditional upvalue, I enclose it's
creation in a block in which a unique local variable is assigned the value
and then enclosed (and thus continues to be mutable).

On the other hand, the first alternative could be implemented, at worst at
the cost of checking an indirection flag. In fact, the indirection flag
only needs to be checked on set (and then only to enforce immutability). In
such an implementation, both bindings and upvalues would be created with
indirection pointers, but in the latter case the indirection pointer would
be an internal pointer within the same memory block, which could include
more than one upvalue. This increases the size of allocation (from the
status quo) but limits the number of allocations to the number of actual
mutable bindings. Maintaining the existing % syntax for such upvalues (in
effect, "anonymous immutable bindings") would be one possibility, although
personally (and I'm certain to be outvoted on this) I would find it more
useful to syntactically mark mutable bindings because their effects need to
be examined more closely when reviewing code.

I fear that the foregoing was disordered and prolix, and apologise if that
is the case. It's a bit late, but I wanted to get some of my thoughts down
for discussion, if anyone is so inclined.

Rici





Reply | Threaded
Open this post in threaded view
|

Re: implementation of lexical scoping

John D. Ramsdell-3
In reply to this post by Roberto Ierusalimschy-5
> This mail explains the general idea we used to implement lexical
> scoping in Lua. Sorry for the delay in answering several questions
> about this.

Thanks.  This is all very interesting.  It appears that the algorithm
you describe adds a cost to every closure creation, a cost borne at
runtime.  The alternative is to perform a two-pass compilation when
generating bytecode, and during the first pass, mark local variables
accessed by nested functions as requiring heap allocation.  The second
pass can generate efficient closures that allocate most of their local
variables on the stack.

It's a classic compile-time vs. runtime trade-off.  It will be
interesting to see the performance results.  For most scripts, all
closure creation will be done when the bytecode is loaded, so I'm
guessing the overhead in time will be small.  I hope the open
references list does not take up too much space.

John

Roberto Ierusalimschy <[hidden email]> writes:

> The main problem for implementing lexical scoping in Lua is that,
> when we first see a variable, we do not know whether it will be used
> by an inner function, and therefore whether it may have to outlive
> the function that created it (as I do not know a better name for
> them, I will keep calling a local variable that is used by an inner
> function an "upvalue").

> The last cost is that, when creating a new closure with open
> references (the most common case), we must insert it in its proper
> place in the list of open closures.

Reply | Threaded
Open this post in threaded view
|

Re: implementation of lexical scoping

Roberto Ierusalimschy-5
On 3 Oct 2001, John D. Ramsdell wrote:

> The alternative is to perform a two-pass compilation when
> generating bytecode, and during the first pass, mark local variables
> accessed by nested functions as requiring heap allocation.  The second
> pass can generate efficient closures that allocate most of their local
> variables on the stack.

I did not understand that. The current implementation already allocate all
their local variables on the stack, initially. Only when there is no other
way (that is, that variable is going out of scope and it is used by some
other closure) the variable goes to the heap.

>
> I hope the open references list does not take up too much space.

No space at all. It is a linked list. We already have a list of all
closures (for GC), so we just keep open references in a separate list.
(Well, it takes an extra "root" in lua_State ;-)

-- Roberto


Reply | Threaded
Open this post in threaded view
|

summary: interactive app to evangelize Lua

Thatcher Ulrich
In reply to this post by Roberto Ierusalimschy-5
I got some good pointers.  Yindo is pretty neat; it's a Lua VM browser
plug-in for Windows web browsers, which provides OpenGL and other APIs to
Lua scripts.  Beats the heck out of Java for download size and graphics
quality.  http://www.yindo.com

Also Nick Trout sent me "doris", a Win32 app that binds Lua w/ GLUT and
glui.

My efforts to build gllua were thwarted -- parts of it seem to be based on
Lua4.0, and other parts on 3.2.

I've pointed my colleagues at Yindo, although I'm not sure if they're
getting anywhere with it.  Unfortunately I don't yet have quite what I
want, which is an *interactive* Lua with simple graphics.  If I could get
gllua to build I might be OK.  I'm not too sure whether GLUT is suitable
for interactive fiddling, but we'll see.

The other thing I'm wondering about is a Lua interactive console that
easily supports multi-line entry.  A GUI text control with a "dostring on
selection" button would be good, or a traditional console UI that collects
input until it sees an "execute now" tag like ";;".  This must be a FAQ
though -- does this feature already exist in standard Lua stuff and I just
don't know about it?

Thanks for the suggestions; further direction is appreciated.  Meanwhile
I'm fiddling around with tolua and luaswig.  An SDL binding would be
practically ideal.  Eventually I'll get somewhere, I hope...

-Thatcher

P.S. I have an incremental lgc.c on the back burner.  I'm pretty sure
it'll work OK but it needs more hacking.


Reply | Threaded
Open this post in threaded view
|

Palm os

Marel
Helo, somebody this list to use lua for programme handheld (palm) with palm
OS ?

sorry my english !
thanks,

sds
Jailson


Reply | Threaded
Open this post in threaded view
|

RE: summary: interactive app to evangelize Lua

Eric Ries
In reply to this post by Thatcher Ulrich
> The other thing I'm wondering about is a Lua interactive console that
> easily supports multi-line entry.  A GUI text control with a "dostring on
> selection" button would be good, or a traditional console UI that collects
> input until it sees an "execute now" tag like ";;".  This must be a FAQ
> though -- does this feature already exist in standard Lua stuff and I just
> don't know about it?

How about Lua support in Emacs? The interactive elisp mode is pretty close
to what you're saying.
Anyone know if this would be hard?


Reply | Threaded
Open this post in threaded view
|

Re: summary: interactive app to evangelize Lua

Thiago Conde Figueiro-2
On Wed, 3 Oct 2001 13:21:05 -0700, "Eric Ries" <[hidden email]> wrote:

ER> How about Lua support in Emacs? The interactive elisp mode is pretty close
ER> to what you're saying.
ER> Anyone know if this would be hard?

	Just my $0.02, on vim you can highlight text (with ^v for instance) and then type "!lua", which will filter the selected text through Lua.  Sometimes I use that one.

regards,

-- 
Thiago Conde Figueiró
Desenvolvedor de sistemas
Cipher Technology
http://www.ciphertech.com.br/

_____
"Segurança em TI - uma especialidade Cipher Technology"

Reply | Threaded
Open this post in threaded view
|

Re: summary: interactive app to evangelize Lua

Bret Mogilefsky-4
In reply to this post by Eric Ries
On Wed, Oct 03, 2001 at 01:21:05PM -0700, Eric Ries wrote:
> How about Lua support in Emacs? The interactive elisp mode is pretty close
> to what you're saying.
> Anyone know if this would be hard?

I wrote a lua-mode.el some time back, and it's since been hacked on by
others to make it better.  It had some functionality for interacting with
an interpreter, but I'm not sure that state of this functionality now.

It looks like the latest version is here, if you're interested:
	http://lua-users.org/wiki/LuaEditorSupport

Bret

Reply | Threaded
Open this post in threaded view
|

Re: implementation of lexical scoping

Roberto Ierusalimschy-5
In reply to this post by RLak
On Tue, 2 Oct 2001 [hidden email] wrote:

> I remain with one question, and one suggestion. The question is probably
> easier: will C closures be presented with the same interface? That is, will
> C closures (need to) use some API call to indirect through to the value of
> an enclosed binding, and therefore have the ability to rebind, or will they
> be presented with "unbound" (resolved) values rather than bindings?

We intend to keep C closures immutable, but maybe we will change the API to
something similar to that proposed by ET. Instead of the upvalues comming
on the top of the stack, they will be accessible through special indices
(generated by a macro "lua_upvalueindex(i)").

> For example, such an implementation might provide an API like
> lua_getbinding(L, i) / lua_setbinding(L, i) which push / pop the stack from
> / to the indexed binding. Moderate compatibility with old code could be
> provided with the API lua_pushbindings(L) which pushes (the values of) all
> enclosed bindings onto the stack, effectively emulating the current
> implementation.

The idea of a lua_pushbindings function seems interesting. We also should
provide a compiler option to keep the old behavior. (That is, to
automatically call "lua_pushbindings" before calling a C function.)

But I don't feel a need for a lua_setbinding. It is all to easy to put a
table as the upvalue, and then put mutable values inside it. Using numerical
indices and the new API, we should be able to get/set those values with a
single API call: «lua_rawgeti(L, lua_upvalueindex(1), MY_KEY);»  In this
way, it is also easy for several functions to share a same table as their
upvalue, so that updates are "global" within those functions.

-- Roberto



Reply | Threaded
Open this post in threaded view
|

Re: summary: interactive app to evangelize Lua

Luiz Henrique de Figueiredo
In reply to this post by Thatcher Ulrich
>a traditional console UI that collects
>input until it sees an "execute now" tag like ";;".

It should be simple to change lua.c to do this.
But lua.c already understand multiple-line input: you just have to end each
line with \ .
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: summary: interactive app to evangelize Lua

Luiz Henrique de Figueiredo
In reply to this post by Thatcher Ulrich
>Also Nick Trout sent me "doris", a Win32 app that binds Lua w/ GLUT and
>glui.

This is new to me. Is "doris" available in the web?

>My efforts to build gllua were thwarted -- parts of it seem to be based on
>Lua4.0, and other parts on 3.2.

Ask Waldemar about this...

>Unfortunately I don't yet have quite what I
>want, which is an *interactive* Lua with simple graphics.

If you can take something for Linux, then I'm pretty sure it'd be easy to
hook Lua with ghostscript and have all the graphics power of PostScript with
the syntax of Lua. Perhaps this is easy to do in Windows, I wouldn't know...
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: Palm os

Jon Kleiser
In reply to this post by Marel
At 16:34 -0300 on 03-10-01, Marel wrote:

Helo, somebody this list to use lua for programme handheld (palm) with palm
OS ?

sorry my english !
thanks,

sds
Jailson

I have enjoyed using PLua. Look here: <http://netpage.em.com.br/mmand/plua.htm>

/Jon

---------------------------------------------------------------------------
Jon Kleiser / ADB ekstern / USIT / University of Oslo / Norway
Mail: [hidden email] / Tel: +47-22 85 28 04 / Fax: +47-22 85 29 70
---------------------------------------------------------------------------

Reply | Threaded
Open this post in threaded view
|

RE: summary: interactive app to evangelize Lua

Philippe Lhoste
In reply to this post by Luiz Henrique de Figueiredo
Thatcher Ulrich wrote:
> I've pointed my colleagues at Yindo, although I'm not sure if they're
> getting anywhere with it.  Unfortunately I don't yet have quite what I
> want, which is an *interactive* Lua with simple graphics.  If I could get
> gllua to build I might be OK.  I'm not too sure whether GLUT is suitable
> for interactive fiddling, but we'll see.
> 
> The other thing I'm wondering about is a Lua interactive console that
> easily supports multi-line entry.  A GUI text control with a "dostring on
> selection" button would be good, or a traditional console UI that collects
> input until it sees an "execute now" tag like ";;".  This must be a FAQ
> though -- does this feature already exist in standard Lua stuff and I just
> don't know about it?

Beside the other editors mentioned here, you can also try SciTE, sometime
mentioned here (http://www.Scintilla.org). It is a Windows and GTK+ editor,
with syntax highlighting of Lua code. It allows to hit F5 to run the content of
the currently opened file (previously saved) with Lua, and it captures the
output, with timing of the command.
Seems close of your needs. I use it for simple tests.
There is also Titmouse, which allows to step in the source.

> Thanks for the suggestions; further direction is appreciated.  Meanwhile
> I'm fiddling around with tolua and luaswig.  An SDL binding would be
> practically ideal.  Eventually I'll get somewhere, I hope...

I though it was already done?
Mmm, searching SDL and Lua on Google, I found this page:
http://diverge.sourceforge.net/ I don't know how mature this project is (seems pre-alpha)... I
don't know if the Lua binding is already working.

Check also the recent FLTK binding, if you don't mind learning a new
programming framework. I know the FLTK natively support an OpenGL widget, so you can
play with it.

See also ClanLib, the game library (http://www.clanlib.org/), which uses Lua
as scripting language.

I have a vaporware, ie. something I would like to do someday (ie. shortly
before the end of time), or would like to see somebody to do it: a binding to
the Ming library (http://www.opaque.net/ming/). It would allows to create
Flash files from Lua, it seems a very cool idea... This library already has PHP,
Python and Ruby bindings, so adding Lua seems feasible.

Regards.

-- 
--._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.--
Philippe Lhoste (Paris -- France)
Professional programmer and amateur artist
http://jove.prohosting.com/~philho/
--´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`--

Sent through GMX FreeMail - http://www.gmx.net


Reply | Threaded
Open this post in threaded view
|

Re: summary: interactive app to evangelize Lua

Nick Trout-2
In reply to this post by Luiz Henrique de Figueiredo
| >Also Nick Trout sent me "doris", a Win32 app that binds Lua w/ GLUT and
| >glui.
| This is new to me. Is "doris" available in the web?

I would like it to be. It was an experiment in Lua binding (started in Lua 3.2
and recently updated to 4.0). I got sidetracked with the wiki, and now I have
just moved house so up to my eyeballs in it. I'd like to find the time to make a
cvs project out of it and plant on the lua-users.org.

Right, quick page knocked up:
http://lua-users.org/wiki/DorisViewer

| >My efforts to build gllua were thwarted -- parts of it seem to be based on
| >Lua4.0, and other parts on 3.2.
|
| Ask Waldemar about this...

I used the binding from gllua to make Doris. I cant remember having much trouble
converting them. I think the main difference if that LUA_OBJECT became LUA_VALUE
(or other way round). ie. its changed from an object reference to a stack index.

All the best,
Nick

Attachment: doris_eg1.jpg
Description: JPEG image

Attachment: Test.lua
Description: Binary data

Reply | Threaded
Open this post in threaded view
|

Re: implementation of lexical scoping

John D. Ramsdell-3
In reply to this post by Roberto Ierusalimschy-5
Roberto Ierusalimschy <[hidden email]> writes:

> On 3 Oct 2001, John D. Ramsdell wrote:

> > I hope the open references list does not take up too much space.
> 
> No space at all. It is a linked list. We already have a list of all
> closures (for GC), so we just keep open references in a separate list.
> (Well, it takes an extra "root" in lua_State ;-)

Ha, yes, I forgot you were using a mark-sweep collector.

> I did not understand that. The current implementation already allocate all
> their local variables on the stack, initially. Only when there is no other
> way (that is, that variable is going out of scope and it is used by some
> other closure) the variable goes to the heap.

Please read my remarks with the assumption that I have read only
selected portions of the 4.1 alpha source code, so my comments may be
off base.  All I was trying to point out is that, if one knew which
variables required heap storage before all code generation begins, one
could implement nested scoping with less overhead at runtime, however,
given the commitment to a that garbage collector already keeps track
of all the closures, the overhead does seems small.

John

Reply | Threaded
Open this post in threaded view
|

Re: Palm os

Danilo Ferraz Daher de Ornellas InfoPAE
In reply to this post by Marel
Hello,

I tried to use Plua but anything happened on the startup when I launched it and gave me an error message. By the way, I've been reading Plua manual and it seems to be quite limited yet. There's a discussion about porting Lua to PalmOS. I suggest you keep your eyes open to this subject. I'm waiting anxious to a better option on PalmOS lua programming.
        If you mess with Plua, try enter in contact with me.

Danilo

At 16:34 3/10/2001 -0300, you wrote:
Helo, somebody this list to use lua for programme handheld (palm) with palm
OS ?

sorry my english !
thanks,

sds
Jailson


Reply | Threaded
Open this post in threaded view
|

Re: Palm os

Victor Bogado da Silva Lins
On Thu, 2001-10-04 at 10:27, Danilo Ferraz Daher de Ornellas InfoPAE
wrote:
> Hello,
> 
>          I tried to use Plua but anything happened on the startup when I 
> launched it and gave me an error message. By the way, I've been reading 
> Plua manual and it seems to be quite limited yet. There's a discussion 
> about porting Lua to PalmOS. I suggest you keep your eyes open to this 
> subject. I'm waiting anxious to a better option on PalmOS lua programming.
>          If you mess with Plua, try enter in contact with me.
> 
> Danilo
> 

	The error message you are seeing is because the software has expired.
Plua is shareware, and it expired a few days ago. I said before that I
would release a source of lua that compiled with the prc-tools. But I
havesecond look at it, and it is quite sincerely a mess. :-)

	I would fix it up and release, but I didn't got much time lately. If
many people are interested in this port I would setup a sourceforge
project for it. What do you think? 


--
[]'s Victor Bogado da Silva Lins
[hidden email]



Reply | Threaded
Open this post in threaded view
|

Re: Palm os

James Hearn
> The error message you are seeing is because the software has expired.
> Plua is shareware, and it expired a few days ago. I said before that I
> would release a source of lua that compiled with the prc-tools. But I
> havesecond look at it, and it is quite sincerely a mess. :-)
>
> I would fix it up and release, but I didn't got much time lately. If
> many people are interested in this port I would setup a sourceforge
> project for it. What do you think?
>
>
> --
> []'s Victor Bogado da Silva Lins
> [hidden email]

I say go for it. I would like to get some [open-source] Lua on my
brand-shiny-new palm. I wouldn't mind hacking at your source either...

--James Hearn


Reply | Threaded
Open this post in threaded view
|

Re: Palm os

Danilo Ferraz Daher de Ornellas InfoPAE
In reply to this post by Victor Bogado da Silva Lins
I haven't been much time free to engage in such a project lately, but I think there would be nice to mess with it on the weekends or in summer vacations (or winter, depending on where you're living ;).
        I'm setting myself to contribute.
        [ ],s

Danilo

At 10:52 4/10/2001 -0300, you wrote:
On Thu, 2001-10-04 at 10:27, Danilo Ferraz Daher de Ornellas InfoPAE
wrote:
> Hello,
>
>          I tried to use Plua but anything happened on the startup when I
> launched it and gave me an error message. By the way, I've been reading
> Plua manual and it seems to be quite limited yet. There's a discussion
> about porting Lua to PalmOS. I suggest you keep your eyes open to this
> subject. I'm waiting anxious to a better option on PalmOS lua programming.
>          If you mess with Plua, try enter in contact with me.
>
> Danilo
>

        The error message you are seeing is because the software has expired.
Plua is shareware, and it expired a few days ago. I said before that I
would release a source of lua that compiled with the prc-tools. But I
havesecond look at it, and it is quite sincerely a mess. :-)

        I would fix it up and release, but I didn't got much time lately. If
many people are interested in this port I would setup a sourceforge
project for it. What do you think?


--
[]'s Victor Bogado da Silva Lins
[hidden email]


12