so you can have a compiled scripting language? I thought they should be interpreted so that you can do things at runtime.
A number of scripting languages (including Lua) do offer the ability to compile to bytecode, and in fact a text chunk will be compiled on the fly when it is loaded. Then there are things like
LuaJIT which is a re-implementation of the VM and a just-in-time compiler for Lua that compiles Lua script down to native machine code and can achieve a quite significant performance increase over "vanilla" Lua.
For me, I like a language if it allows me to do what I need to do without a whole bunch of hoop-jumping to do it. Lua+LuaJIT is my preferred high-level tool loadout, and C++ is my preferred low-level tool. The C++ layer provides the performance critical stuff (rendering abstraction, etc...) while the Lua layer provides the expressiveness necessary to quickly prototype and implement the high level things like enemy AI, spell and skill execution, and so forth.
bytecode, at least, is pretty much standard for scripting languages.
usually, if bytecode or similar is not used, and input text or ASTs are executed directly, the language is "impractically slow" (often around 10k or 100k times slower than native), whereas typically with a bytecode interpreter, it is usually possible to get within 50x-100x of native.
though, people have used some non-bytecode representations for interpretation with some success (typically linked lists or trees of objects), bytecode also has the advantage of being easier to store on disk and use later, and can also slightly better decouple the front-end logic from the back-end interpreter logic.
a JIT just takes things a step further, typically compiling the bytecode to native machine-code at runtime, then the native-code version is executed.
although a JIT can't usually optimize nearly as aggressively as an offline / batch compiler, it can usually get "pretty close" to native code speeds (it depends more on language and VM design and similar than the specifics of the JIT).
otherwise, a lot also comes down to language design and how the compilers and tools are implemented:
C and C++ are generally compiled directly to machine code, via offline tools;
Java and C# are generally compiled to bytecode (via offline tools), then the VM will JIT-compile them to native code;
script-languages are typically compiled to bytecode at run-time, then sometimes JIT compiled.
experimentally, it is possible to compile C and C++ dynamically (like a script language), however some things in the language design (particularly headers and the way declarations and similar work, ...) get in the way of implementing a fast compiler. also if implemented in a standards-conformant way, it is also "poor" as a scripting language, namely that it requires a lot of code to do things (making it less useful for interactive entry), and (short of internal fudging), it is far too easy to crash the host application. addressing these issues would break the standard in some fairly fundamental ways.
with Java and C# it is a lot closer, mostly boiling down to language design subtleties and awkwardness tradeoffs.
with script-languages, they are typically a lot better for small fragments and are typically very dynamic.
however, many often skimp out on lots of things needed for more serious coding or for general scalability, so one is left with a language often lacking much good support for being able to use libraries, and turning into an awful mess as code moves past a few thousand lines (this is where more traditional "compiled language" features tend to do well).
a compromise is possible though.
if you have a language that can both be batch compiled, as well as loaded as script-code fragments and dynamically compiled at run-time, supports features to be usable from small code fragments (as can be written interactively), supports both static and dynamic typing, supports things like packages/namespaces and classes, can more easily use code from C libraries (*), ... then things can be a little nicer.
*: though in my VM, this requires use of batch tools to process headers and, in some cases, to generate stub-code (usually for exporting stuff to C land).
usually this can be combined with any native code into the form of a DLL. usually for imports from C land, it isn't very fussy (it imports pretty much everything from the seen headers which are reachable via DLL imports). though, some things in C-land have been black-listed (mostly for security reasons).
IME, static types and classes, while not particularly advantageous for small code fragments, become a fair bit more useful as code gets larger (for organization, and for things like the compiler helpfully informing the programmer that they messed up their argument list or similar). they also have an advantage of being easier to optimize. though, also having both static and dynamic types can be fairly useful (dynamic types can be fit into a static type system by seeing them as a type-of-type, so in many practical regards, seeing static and dynamic types as opposed is a false dichotomy, and can offer an advantage for cases where, in fact, one is actually dealing with multiple types at run-time).
however, to support both use-cases, a language tends to require both sets of feature-sets, typically resulting in something a little bigger and more complex than either would be on its own (a scripting language with the full feature-set of a traditional compiled language thrown on, or seen the other way, a traditional compiled language having to deal with a lot of the added complexities of supporting dynamic features).
for example, to support both the feature-sets of C and JavaScript, the actual compiler and VM ends up a bit more complicated than either a C or JavaScript compiler or VM would be on their own, and a superset of C, JavaScript, and C#, adds that much more. though, on the positive side, the language can do a little more nifty stuff... (and complexity isn't usually as big of a deal in-practice as it is in-concept).