Can we (Is it possible to) make a compiler for any dynamic/script/interpreter language,
Yes, it is.
Note that there is no such thing as an "interpreter" language. An interpreter is a strategy for implementing a language, it is not a property of the language itself. It is a property of … well … the interpreter.
Also note that the Second Futamura Projection actually gives a way of automatically deriving a compiler from an interpreter, so in a theoretical sense, once you have an interpreter, you also have a compiler as well.
In a practical sense, both the Truffle language implementation framework and the RPython language implementation framework have already successfully demonstrated automatically creating compilers from interpreters, e.g. Truffle has interpreters for Ruby, Python, JavaScript, R, Smalltalk, Newspeak, and even LLVM bitcode, and RPython has implementations for Python and Monte, and used to have working implementations of Smalltalk, Prolog, and many others.
like Lua, Python, JavaScript etc,
Yes. In fact, all of those languages have compilers. There is not a single mainstream implementation of Lua, Python, or JavaScript that does not have a compiler.
For example,
- LuaVM compiles Lua to LuaVM byte code,
- LuaJIT compiles LuaVM byte code to native machine code,
- CPython compiles Python to CPython byte code,
- Jython compiles Python to JVM byte code,
- IronPython compiles Python to DLR Trees,
- GraalPython is a strange one, it parses Python into Truffle ASTs, and then interprets the Truffle ASTs with a specializing interpreter, which is essentially equivalent to compiling the Truffle ASTs (it's related to Partial Evaluation and Abstract Interpretation (aka Supercompilation)),
- PyPy is another strange one, it compiles Python to RPython byte code, interprets that RPython byte code, and uses a tracing compiler to compile a specialized version of the interpreter which can only interpret that one program, which is essentially equivalent to compiling the RPython byte code (it's related to Partial Evaluation and Abstract Interpretation (aka Supercompilation)),
- the original version of Google's V8 JavaScript engine compiled JavaScript straight to native machine code, later versions added a second compiler, so that there was a fast compiler that produced less optimized native machine code, and a slow compiler that produced more optimized native machine code; current versions only have one compiler but also an interpreter,
- SpiderMonkey compiles JavaScript to SpiderMonkey byte code, then interprets part of that byte code, and compiles other parts to native machine code,
- SquirrelFish Extreme compiles JavaScript to SquirrelFish Extreme byte code, then interprets part of that byte code, and compiles other parts to native machine code,
- Nashorn compiles JavaScript to JVM byte code,
- dyn.js compiles JavaScript to JVM byte code,
- IronJS compiles JavaScript to DLR Trees,
- …
As you can see, every Lua implementation, Python implementation, and JavaScript implementation in widespread use, has at least one compiler – some even have more than one.
to compile it to native machine code,
That is an extremely narrow definition of "compile". Compilation only means translating from one language to another language while preserving the meaning of the program. There is no requirement that the target language be native machine code.
And what does "native machine code" mean, anyway? I am writing this on a MacBook Air M1 with an ARM processor, running the AMD64 version of my browser in the Rosetta 2 emulator. So, is the AMD64 code of the browser native machine code or not? What if you run Linux in an x86 emulator on a Java Virtual Machine running on a MIPS emulator running on a SPARC workstation? What if you run Linux in an x86 emulator running on a Java Virtual Machine running natively on a Java CPU?
However, as you can see in my list above, there are indeed several implementations that will actually compile to "native machine code" (e.g. AMD64, ARM, POWER, MIPS, …), for example LuaJIT, Jython when running on e.g. HotSpot, GraalVM, or J9, IronPython when running on Mono, CoreCLR, or CLR, all JavaScript implementations in all major browsers will compile JavaScript to native machine code; I believe V8 even compiles Regexes to native machine code.
Or there is a special case/feature make it impossible to write compiler for it?
No. Some people say that a feature like eval
makes it impossible, but that is not true: you could write an interpreter, and compile calls to eval
into calls to that interpreter. Or, much simpler, since you already have a compiler anyway: compile calls to eval
into a call to the compiler followed by a call to the output of the compiler.
Other people say that macros make it impossible, but that is not true either. In fact, it is just the dual to the problem with eval
: instead of making the compiler a part of the program, you make the program a part of the compiler.
In fact, problems like these are not unique to dynamic languages. C++ Templates are Turing-complete, which means that compiling them is equivalent to running a potentially infinite program. Similarly, some extensions to Haskell's type system are Turing-complete, as is Scala 2's type system. Overload resolution in C# is famously NP-complete, so while it is guaranteed that compiling a C# program always terminates, I can easily write a program that takes longer than the heat death of the universe to compile.
Does the fact that compiling some legal C++ programs takes an infinite amount of time mean that you can't have a compiler for C++? Does the fact that compiling some legal C# programs takes longer than the lifetime of the universe mean that you can't have a compiler for C#?