![Page 1: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/1.jpg)
Small is Beautiful: the designof Lua
Roberto IerusalimschyPUC-Rio
![Page 2: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/2.jpg)
Language design• many tradeoffs
• similar to any other design process• designers seldom talk about them
• what a language is not good for
![Page 3: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/3.jpg)
Typical tradeoffs• security x flexibility
• static verification• readability x conciseness• performance x abstraction
• specially in an interpreted language
![Page 4: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/4.jpg)
A special tradeoff• simplicity x almost everything else• several other conflicts can be solved by
adding complexity• smarter algorithms• multiple mechanisms ("There's more than
one way to do it")
![Page 5: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/5.jpg)
Lua• a scripting language• simplicity as one of its main goals
• small size too• "real" language
• many users and uses• tricky balance between "as simple as
possible" x "but not simpler"
![Page 6: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/6.jpg)
Lua uses• niche in games
• "Is Lua the ultimate game scriptinglanguage?" (GDC 2010)
• embedded devices• cameras (Canon), keyboards (Logitech),
printers (Olivetty & Océ)• scripting applications
• Wireshark, Snort, Nmap
![Page 7: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/7.jpg)
Lua main goals• simplicity/small size• portability• "embedability"
• scripting!
![Page 8: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/8.jpg)
Small size
• source lines of code (proxy for complexity)
![Page 9: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/9.jpg)
Portability• runs on most machines we ever heard of
• Symbian, DS, PSP, PS3 (PPE & SPE),Android, iPhone, etc.
• written in ANSI C ∩ ANSI C++• avoids #ifdefs• avoids dark corners of the standard
![Page 10: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/10.jpg)
Embedability• provided as a library• simple API
• simple types• low-level operations• stack model
• embedded in C/C++, Java, Fortran, C#,Perl, Ruby, Python, Ada, etc.
![Page 11: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/11.jpg)
function fact (n) if n == 0 then return 1 else return n * fact(n - 1) endend
function fact (n) local f = 1 for i=2,n do f = f * i end return fend
An overview of Lua• Conventional syntax
• somewhat verbose
![Page 12: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/12.jpg)
An overview of Lua• semantically quite similar to Scheme• dynamically typed• functions are first-class values with static
scoping
![Page 13: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/13.jpg)
BTW...function fact (n) local f = 1 for i=2,n do f = f * i; end return fend
fact = function (n) local f = 1 for i=2,n do f = f * i; end return f end
syntactic sugar
![Page 14: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/14.jpg)
An overview of Lua• proper tail recursive• Lua does not have full continuations, but
have one-shot continuations• in the form of coroutines
![Page 15: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/15.jpg)
Design• tables• coroutines• the Lua-C API
![Page 16: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/16.jpg)
Tables• associative arrays
• any value as key• only data-structure mechanism in Lua
![Page 17: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/17.jpg)
Why tables• VDM: maps, sequences, and (finite) sets
• collections• any one can represent the others• only maps represent the others with
simple and efficient code
![Page 18: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/18.jpg)
Data structures• tables implement most data structures in
a simple and efficient way• records: syntactical sugar t.x fort["x"]:
t = {}t.x = 10t.y = 20print(t.x, t.y)print(t["x"], t["y"])
![Page 19: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/19.jpg)
Data Structures• arrays: integers as indices
• sets: elements as indices
a = {}for i=1,n do a[i] = 0 end
t = {}t[x] = true -- t = t ∪ {x}if t[x] then -- x ∈ t? ...
![Page 20: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/20.jpg)
Other constructions• tables also implement modules
• print(math.sin(3))
• tables also implement objects• with the help of a delegation mechanism
and some syntactic sugar
![Page 21: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/21.jpg)
function a:foo (x) ...end
a.foo = function (self,x) ...end
a:foo(x) a.foo(a,x)
Objects• first-class functions + tables ≈ objects• syntactical sugar for methods
• handles self
![Page 22: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/22.jpg)
Delegation• field-access delegation (instead of
method-call delegation)• when a delegates to b, any field absent
in a is got from b• a[k] becomes (a[k] or b[k])
• allows prototype-based and class-basedobjects
• allows single inheritance
![Page 23: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/23.jpg)
Delegation at work
a:foo(x) a.foo(a,x)
a: k = 0delegate:
"class": foo = function ...
![Page 24: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/24.jpg)
Tables: problems• the implementation of a concept with
tables is not as good as a primitiveimplementation• access control in objects• length in sequences
• different implementations confoundprogrammers• DIY object systems
![Page 25: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/25.jpg)
Coroutines• old and well-established concept, but
with several variations• variations not equivalent
• several languages implement restrictedforms of coroutines that are not equivalentto one-shot continuations
![Page 26: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/26.jpg)
Coroutines in Lua
c = coroutine.create(function () print(1) coroutine.yield() print(2) end)
coroutine.resume(c) --> 1coroutine.resume(c) --> 2
![Page 27: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/27.jpg)
Coroutines in Lua• first-class values
• in particular, we may invoke a coroutinefrom any point in a program
• stackful• a coroutine can transfer control from inside
any number of function calls• asymmetrical
• different commands to resume and to yield
![Page 28: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/28.jpg)
Coroutines in Lua• simple and efficient implementation
• the easy part of multithreading• first class + stackful = complete
coroutines• equivalent to one-shot continuations• we can implement call/1cc
• coroutines present one-shotcontinuations in a format that is morefamiliar to most programmers
![Page 29: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/29.jpg)
Coroutines x continuations• most uses of continuations can be
coded with coroutines• "who has the main loop" problem
• producer-consumer• extending x embedding
• iterators x generators• the same-fringe problem
• collaborative multithreading
![Page 30: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/30.jpg)
Coroutines x continuations• multi-shot continuations are more
expressive than coroutines• some techniques need code
reorganization to be solved withcoroutines or one-shot continuations• oracle functions
![Page 31: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/31.jpg)
The Lua-C API• Lua is a library
• formally, an ADT (a quite complex one)• 79 functions
• the entire language actually describesthe argument to one function of thatlibrary: load• load gets a stream with source code and
returns a function that is semanticallyequivalent to that code
![Page 32: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/32.jpg)
The Lua-C API• most APIs use some kind of "Value" type
in C• PyObject (Python), jobject (JNI)
• problem: garbage collection• Python: explicit manipulation of reference
counts• JNI: local and global references
• too easy to create dangling referencesand memory leaks
![Page 33: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/33.jpg)
The Lua-C API• Lua API has no "LuaObject" type• a Lua object lives only inside Lua• two structures keep objects used by C:
• the stack• the registry
![Page 34: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/34.jpg)
The Stack• keep all Lua objects in use by a C function• injection functions
• convert a C value into a Lua value• push the result into the stack
• projection functions• convert a Lua value into a C value• get the Lua value from anywhere in the stack
![Page 35: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/35.jpg)
/* calling f("hello", 4.5) */lua_getglobal(L, "f");lua_pushstring(L, "hello");lua_pushnumber(L, 4.5);lua_call(L, 2, 1);if (lua_isnumber(L, -1)) printf("%f\n", lua_getnumber(L, -1));
The Stack• example: calling a Lua function from C
• push function, push arguments, do the call,get result from the stack
![Page 36: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/36.jpg)
/* calling f("hello", 4.5) */lua_getglobal(L, "f");lua_pushstring(L, "hello");lua_pushnumber(L, 4.5);lua_call(L, 2, 1);if (lua_isnumber(L, -1)) printf("%f\n", lua_getnumber(L, -1));
The Stack• example: calling a Lua function from C
• push function, push arguments, do the call,get result
![Page 37: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/37.jpg)
/* calling f("hello", 4.5) */lua_getglobal(L, "f");lua_pushstring(L, "hello");lua_pushnumber(L, 4.5);lua_call(L, 2, 1);if (lua_isnumber(L, -1)) printf("%f\n", lua_getnumber(L, -1));
The Stack• example: calling a Lua function from C
• push function, push arguments, do the call,get result
![Page 38: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/38.jpg)
/* calling f("hello", 4.5) */lua_getglobal(L, "f");lua_pushstring(L, "hello");lua_pushnumber(L, 4.5);lua_call(L, 2, 1);if (lua_isnumber(L, -1)) printf("%f\n", lua_getnumber(L, -1));
The Stack• example: calling a Lua function from C
• push function, push arguments, do the call,get result
![Page 39: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/39.jpg)
/* calling f("hello", 4.5) */lua_getglobal(L, "f");lua_pushstring(L, "hello");lua_pushnumber(L, 4.5);lua_call(L, 2, 1);if (lua_isnumber(L, -1)) printf("%f\n", lua_getnumber(L, -1));
The Stack• example: calling a Lua function from C
• push function, push arguments, do the call,get result from the stack
![Page 40: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/40.jpg)
The Stack• example: calling a C function from Lua
• get arguments from the stack, docomputation, push arguments into the stack
static int l_sqrt (lua_State *L) { double n = luaL_checknumber(L, 1); lua_pushnumber(L, sqrt(n)); return 1; /* number of results */}
![Page 41: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/41.jpg)
The Stack• example: calling a C function from Lua
• get arguments from the stack, docomputation, push arguments into the stack
static int l_sqrt (lua_State *L) { double n = luaL_checknumber(L, 1); lua_pushnumber(L, sqrt(n)); return 1; /* number of results */}
![Page 42: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/42.jpg)
The Stack• example: calling a C function from Lua
• get arguments from the stack, docomputation, push arguments into the stack
static int l_sqrt (lua_State *L) { double n = luaL_checknumber(L, 1); lua_pushnumber(L, sqrt(n)); return 1; /* number of results */}
![Page 43: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/43.jpg)
The Stack• example: calling a C function from Lua
• get arguments from the stack, docomputation, push arguments into the stack
static int l_sqrt (lua_State *L) { double n = luaL_checknumber(L, 1); lua_pushnumber(L, sqrt(n)); return 1; /* number of results */}
![Page 44: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/44.jpg)
The Registry• sometimes, a reference to a Lua object
must outlast a C function• NewGlobalRef in the JNI
• the registry is a regular Lua table alwaysaccessible by the API• no new concepts• to create a new "global reference", store the
Lua object at a unique key in the registryand keeps the key
![Page 45: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/45.jpg)
The Lua-C API: problems• too low level
• some operations need too many calls• stack-oriented programming sometimes
is confusing• what is where
• no direct mapping of complex types• may be slow for large values
![Page 46: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/46.jpg)
Conclusions• any language design involves conflicting
goals• designers must solve conflicts
• consciously or not• to get simplicity we must give something
• performance, easy of use, particularfeatures or libraries,
![Page 47: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/47.jpg)
Conclusions• simplicity is not an absolute goal• it must be pursued incessantly as the
language evolve• it is much easier to add a feature than to
remove one• start simple, grow as needed
• it is very hard to anticipate allimplications of a new feature• clash with future features
![Page 48: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/48.jpg)
Conclusions• "Mechanisms instead of policies"
• e.g., delegation• effective way to avoid tough decisions• this itself is a decision...
![Page 49: Small is Beautiful: the design of Lua · The Registry •sometimes, a reference to a Lua object must outlast a C function •NewGlobalRef in the JNI •the registry is a regular Lua](https://reader031.vdocuments.net/reader031/viewer/2022022110/5c0e497b09d3f258548cc936/html5/thumbnails/49.jpg)
www.lua.org