3 ms·
OK, I see your point -- in terms of language complexity. "Strict subset" wasn't precise. Sorry. My impression comes from when I took a comparative programming
by SomeCallMeTim 10y ago
OK, I see your point -- in terms of language complexity. "Strict subset" wasn't precise. Sorry.
My impression comes from when I took a comparative programming languages class and we developed a Pascal interpreter.
I then took compiler design and over the course of two quarters implemented a compiler. Note I said "over TWO quarters." It took twice the time to create the compiler than the interpreter. A simple interpreter absolutely can be easier to write than a simple compiler.
That's what I'm talking about: The minimal requirements for writing an interpreter using a high level language (we used C++ -- this was 1988, so C++ before templates were a part of the language) are less complex than the minimal requirements for writing a compiler using a high level language (also C++, though with the addition of YACC and LEX).
The interpreter has to include a VM state, sure. But that state can be implemented in the state of the language you're using. I've written simple interpreters a half dozen times now -- very simple ones that I needed to run animations in games, or to script AI opponents, or to script UI layout and trigger UI interactions. The interpreted language was frequently barely Turing Complete, but I guarantee you that writing a compiler that spits out machine (or even assembly) code is much harder.
Among other things, you can rely on the host language itself to handle recursion. You can also create a "map" of global variables and, when the interpreter says to set a value, pull it out of the map.
I'm overgeneralizing for brevity, but I hope you see what I'm saying. Writing an interpreter for a simple language that maps well into the host language can actually be trivial -- the host language itself can provide much of the implementation.
Writing a compiler doesn't allow such shortcuts. You must code to the target machine architecture, not an arbitrarily complex (but well-mapped to the language) architecture stored in high level data structures. You must do all the hard work of reducing high level statements down to a sequence of low-level instructions.
If you're talking about writing an interpreter that spits out VM instructions (where it's basically performing a compile-to-vm), then sure, you're right: In that case you're writing a compiler and a VM and calling it an interpreter. Any real interpreter today would likely work this way, if only to be able to take advantage of LLVM -- and if you can use that, you can write your interpreter at exactly the same level of complexity as writing a compiler. The tasks would be equivalent.
If you're talking a transpiler that takes one high level language and spits out another high level language (thinking Babel, TypeScript, CoffeeScript, or even the early C++-to-C conversion tools), then sure, that would be even simpler than writing an interpreter.
If your task is just to write an interpreter, and the source and destinations languages are similar enough, then you can use shortcuts writing the interpreter (similar to writing the transpiler) that you just can't use writing a full compiler.