You have lexical scope, anonymous and first class functions, and dynamic typing.
The parser is as simple as possible (LL(1), ie top down, without backtracking), and doesn't do any magic like declaration hoisting or semi-column insertion.
The only falsy objects are `false` and `nil`. `==` doesn't do any type conversion.
The addition `+` and concatenation `..` operators are different.
Some infix operators do automatic type conversion ( +, -, *, /, %, ..) between strings and numbers, but that's about it for weak typing. There's no octal to decimal conversion for strings with prepended zeros.
There's only one way to define objects. The `self` parameter is explicit for methods, but there's syntactic sugar to pass it automatically.
Scoping is similar: variables are global by default, which allows precise scoping for closures.
== Where the languages diverge:
You can do some magic with metatables and environments, but once again, the mental model is extremely simple.
The other major differences are coroutines (cooperative threads) and a "generic" `for` loop that allows to build custom idiomatic iterators.
At last, the array indexing starts at 1. This may sound like heresy to devout Dijkstra followers, but it's really not a big deal in practice.
: http://www.lua.org/pil/ by Roberto Ierusalimschy, one of the author of the language.
In a video I saw, by one of the designers of the languages stated that this was the original purpose (engineers using this instead of C, with the low level details done by programming team in C).
Furthermore, the VM binary weights 200k. It is extremely portable (it uses the common subset of C89 and C++), and its code is a pleasure to hack.